1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
30 var gopathInstallDir
, gorootInstallDir
, suffix
string
32 // This is the smallest set of packages we can link into a shared
33 // library (runtime/cgo is built implicitly).
34 var minpkgs
= []string{"runtime", "sync/atomic"}
35 var soname
= "libruntime,sync-atomic.so"
37 // run runs a command and calls t.Errorf if it fails.
38 func run(t
*testing
.T
, msg
string, args
...string) {
39 c
:= exec
.Command(args
[0], args
[1:]...)
40 if output
, err
:= c
.CombinedOutput(); err
!= nil {
41 t
.Errorf("executing %s (%s) failed %s:\n%s", strings
.Join(args
, " "), msg
, err
, output
)
45 // goCmd invokes the go tool with the installsuffix set up by TestMain. It calls
46 // t.Fatalf if the command fails.
47 func goCmd(t
*testing
.T
, args
...string) {
48 newargs
:= []string{args
[0], "-installsuffix=" + suffix
}
49 if testing
.Verbose() {
50 newargs
= append(newargs
, "-x")
52 newargs
= append(newargs
, args
[1:]...)
53 c
:= exec
.Command("go", newargs
...)
56 if testing
.Verbose() {
57 fmt
.Printf("+ go %s\n", strings
.Join(newargs
, " "))
61 output
= []byte("(output above)")
63 output
, err
= c
.CombinedOutput()
67 t
.Fatalf("executing %s failed %v:\n%s", strings
.Join(c
.Args
, " "), err
, output
)
69 log
.Fatalf("executing %s failed %v:\n%s", strings
.Join(c
.Args
, " "), err
, output
)
74 // TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit).
75 func testMain(m
*testing
.M
) (int, error
) {
76 // Because go install -buildmode=shared $standard_library_package always
77 // installs into $GOROOT, here are some gymnastics to come up with a unique
78 // installsuffix to use in this test that we can clean up afterwards.
79 myContext
:= build
.Default
80 runtimeP
, err
:= myContext
.Import("runtime", ".", build
.ImportComment
)
82 return 0, fmt
.Errorf("import failed: %v", err
)
84 for i
:= 0; i
< 10000; i
++ {
85 try
:= fmt
.Sprintf("%s_%d_dynlink", runtimeP
.PkgTargetRoot
, rand
.Int63())
86 err
= os
.Mkdir(try
, 0700)
91 gorootInstallDir
= try
96 return 0, fmt
.Errorf("can't create temporary directory: %v", err
)
98 if gorootInstallDir
== "" {
99 return 0, errors
.New("could not create temporary directory after 10000 tries")
101 if testing
.Verbose() {
102 fmt
.Printf("+ mkdir -p %s\n", gorootInstallDir
)
104 defer os
.RemoveAll(gorootInstallDir
)
106 // Some tests need to edit the source in GOPATH, so copy this directory to a
107 // temporary directory and chdir to that.
108 scratchDir
, err
:= ioutil
.TempDir("", "testshared")
110 return 0, fmt
.Errorf("TempDir failed: %v", err
)
112 if testing
.Verbose() {
113 fmt
.Printf("+ mkdir -p %s\n", scratchDir
)
115 defer os
.RemoveAll(scratchDir
)
116 err
= filepath
.Walk(".", func(path
string, info os
.FileInfo
, err error
) error
{
117 scratchPath
:= filepath
.Join(scratchDir
, path
)
122 if testing
.Verbose() {
123 fmt
.Printf("+ mkdir -p %s\n", scratchPath
)
125 return os
.Mkdir(scratchPath
, info
.Mode())
127 fromBytes
, err
:= ioutil
.ReadFile(path
)
131 if testing
.Verbose() {
132 fmt
.Printf("+ cp %s %s\n", path
, scratchPath
)
134 return ioutil
.WriteFile(scratchPath
, fromBytes
, info
.Mode())
138 return 0, fmt
.Errorf("walk failed: %v", err
)
140 os
.Setenv("GOPATH", scratchDir
)
141 if testing
.Verbose() {
142 fmt
.Printf("+ export GOPATH=%s\n", scratchDir
)
144 myContext
.GOPATH
= scratchDir
145 if testing
.Verbose() {
146 fmt
.Printf("+ cd %s\n", scratchDir
)
150 // All tests depend on runtime being built into a shared library. Because
151 // that takes a few seconds, do it here and have all tests use the version
153 suffix
= strings
.Split(filepath
.Base(gorootInstallDir
), "_")[2]
154 goCmd(nil, append([]string{"install", "-buildmode=shared"}, minpkgs
...)...)
156 myContext
.InstallSuffix
= suffix
+ "_dynlink"
157 depP
, err
:= myContext
.Import("depBase", ".", build
.ImportComment
)
159 return 0, fmt
.Errorf("import failed: %v", err
)
161 gopathInstallDir
= depP
.PkgTargetRoot
165 func TestMain(m
*testing
.M
) {
168 // Some of the tests install binaries into a custom GOPATH.
169 // That won't work if GOBIN is set.
172 exitCode
, err
:= testMain(m
)
179 // The shared library was built at the expected location.
180 func TestSOBuilt(t
*testing
.T
) {
181 _
, err
:= os
.Stat(filepath
.Join(gorootInstallDir
, soname
))
187 func hasDynTag(f
*elf
.File
, tag elf
.DynTag
) bool {
188 ds
:= f
.SectionByType(elf
.SHT_DYNAMIC
)
200 t
= elf
.DynTag(f
.ByteOrder
.Uint32(d
[0:4]))
203 t
= elf
.DynTag(f
.ByteOrder
.Uint64(d
[0:8]))
213 // The shared library does not have relocations against the text segment.
214 func TestNoTextrel(t
*testing
.T
) {
215 sopath
:= filepath
.Join(gorootInstallDir
, soname
)
216 f
, err
:= elf
.Open(sopath
)
218 t
.Fatal("elf.Open failed: ", err
)
221 if hasDynTag(f
, elf
.DT_TEXTREL
) {
222 t
.Errorf("%s has DT_TEXTREL set", soname
)
226 // The shared library does not contain symbols called ".dup"
227 func TestNoDupSymbols(t
*testing
.T
) {
228 sopath
:= filepath
.Join(gorootInstallDir
, soname
)
229 f
, err
:= elf
.Open(sopath
)
231 t
.Fatal("elf.Open failed: ", err
)
234 syms
, err
:= f
.Symbols()
236 t
.Errorf("error reading symbols %v", err
)
239 for _
, s
:= range syms
{
240 if s
.Name
== ".dup" {
241 t
.Fatalf("%s contains symbol called .dup", sopath
)
246 // The install command should have created a "shlibname" file for the
247 // listed packages (and runtime/cgo, and math on arm) indicating the
248 // name of the shared library containing it.
249 func TestShlibnameFiles(t
*testing
.T
) {
250 pkgs
:= append([]string{}, minpkgs
...)
251 pkgs
= append(pkgs
, "runtime/cgo")
252 if runtime
.GOARCH
== "arm" {
253 pkgs
= append(pkgs
, "math")
255 for _
, pkg
:= range pkgs
{
256 shlibnamefile
:= filepath
.Join(gorootInstallDir
, pkg
+".shlibname")
257 contentsb
, err
:= ioutil
.ReadFile(shlibnamefile
)
259 t
.Errorf("error reading shlibnamefile for %s: %v", pkg
, err
)
262 contents
:= strings
.TrimSpace(string(contentsb
))
263 if contents
!= soname
{
264 t
.Errorf("shlibnamefile for %s has wrong contents: %q", pkg
, contents
)
269 // Is a given offset into the file contained in a loaded segment?
270 func isOffsetLoaded(f
*elf
.File
, offset
uint64) bool {
271 for _
, prog
:= range f
.Progs
{
272 if prog
.Type
== elf
.PT_LOAD
{
273 if prog
.Off
<= offset
&& offset
< prog
.Off
+prog
.Filesz
{
281 func rnd(v
int32, r
int32) int32 {
294 func readwithpad(r io
.Reader
, sz
int32) ([]byte, error
) {
295 data
:= make([]byte, rnd(sz
, 4))
296 _
, err
:= io
.ReadFull(r
, data
)
311 // Read all notes from f. As ELF section names are not supposed to be special, one
312 // looks for a particular note by scanning all SHT_NOTE sections looking for a note
313 // with a particular "name" and "tag".
314 func readNotes(f
*elf
.File
) ([]*note
, error
) {
316 for _
, sect
:= range f
.Sections
{
317 if sect
.Type
!= elf
.SHT_NOTE
{
322 var namesize
, descsize
, tag
int32
323 err
:= binary
.Read(r
, f
.ByteOrder
, &namesize
)
328 return nil, fmt
.Errorf("read namesize failed: %v", err
)
330 err
= binary
.Read(r
, f
.ByteOrder
, &descsize
)
332 return nil, fmt
.Errorf("read descsize failed: %v", err
)
334 err
= binary
.Read(r
, f
.ByteOrder
, &tag
)
336 return nil, fmt
.Errorf("read type failed: %v", err
)
338 name
, err
:= readwithpad(r
, namesize
)
340 return nil, fmt
.Errorf("read name failed: %v", err
)
342 desc
, err
:= readwithpad(r
, descsize
)
344 return nil, fmt
.Errorf("read desc failed: %v", err
)
346 notes
= append(notes
, ¬e
{name
: string(name
), tag
: tag
, desc
: string(desc
), section
: sect
})
352 func dynStrings(t
*testing
.T
, path
string, flag elf
.DynTag
) []string {
353 f
, err
:= elf
.Open(path
)
355 t
.Fatalf("elf.Open(%q) failed: %v", path
, err
)
358 dynstrings
, err
:= f
.DynString(flag
)
360 t
.Fatalf("DynString(%s) failed on %s: %v", flag
, path
, err
)
365 func AssertIsLinkedToRegexp(t
*testing
.T
, path
string, re
*regexp
.Regexp
) {
366 for _
, dynstring
:= range dynStrings(t
, path
, elf
.DT_NEEDED
) {
367 if re
.MatchString(dynstring
) {
371 t
.Errorf("%s is not linked to anything matching %v", path
, re
)
374 func AssertIsLinkedTo(t
*testing
.T
, path
, lib
string) {
375 AssertIsLinkedToRegexp(t
, path
, regexp
.MustCompile(regexp
.QuoteMeta(lib
)))
378 func AssertHasRPath(t
*testing
.T
, path
, dir
string) {
379 for _
, tag
:= range []elf
.DynTag
{elf
.DT_RPATH
, elf
.DT_RUNPATH
} {
380 for _
, dynstring
:= range dynStrings(t
, path
, tag
) {
381 for _
, rpath
:= range strings
.Split(dynstring
, ":") {
382 if filepath
.Clean(rpath
) == filepath
.Clean(dir
) {
388 t
.Errorf("%s does not have rpath %s", path
, dir
)
391 // Build a trivial program that links against the shared runtime and check it runs.
392 func TestTrivialExecutable(t
*testing
.T
) {
393 goCmd(t
, "install", "-linkshared", "trivial")
394 run(t
, "trivial executable", "./bin/trivial")
395 AssertIsLinkedTo(t
, "./bin/trivial", soname
)
396 AssertHasRPath(t
, "./bin/trivial", gorootInstallDir
)
399 // Build a trivial program in PIE mode that links against the shared runtime and check it runs.
400 func TestTrivialExecutablePIE(t
*testing
.T
) {
401 goCmd(t
, "build", "-buildmode=pie", "-o", "trivial.pie", "-linkshared", "trivial")
402 run(t
, "trivial executable", "./trivial.pie")
403 AssertIsLinkedTo(t
, "./trivial.pie", soname
)
404 AssertHasRPath(t
, "./trivial.pie", gorootInstallDir
)
407 // Build a division test program and check it runs.
408 func TestDivisionExecutable(t
*testing
.T
) {
409 goCmd(t
, "install", "-linkshared", "division")
410 run(t
, "division executable", "./bin/division")
413 // Build an executable that uses cgo linked against the shared runtime and check it
415 func TestCgoExecutable(t
*testing
.T
) {
416 goCmd(t
, "install", "-linkshared", "execgo")
417 run(t
, "cgo executable", "./bin/execgo")
420 func checkPIE(t
*testing
.T
, name
string) {
421 f
, err
:= elf
.Open(name
)
423 t
.Fatal("elf.Open failed: ", err
)
426 if f
.Type
!= elf
.ET_DYN
{
427 t
.Errorf("%s has type %v, want ET_DYN", name
, f
.Type
)
429 if hasDynTag(f
, elf
.DT_TEXTREL
) {
430 t
.Errorf("%s has DT_TEXTREL set", name
)
434 func TestTrivialPIE(t
*testing
.T
) {
435 name
:= "trivial_pie"
436 goCmd(t
, "build", "-buildmode=pie", "-o="+name
, "trivial")
437 defer os
.Remove(name
)
438 run(t
, name
, "./"+name
)
442 func TestCgoPIE(t
*testing
.T
) {
444 goCmd(t
, "build", "-buildmode=pie", "-o="+name
, "execgo")
445 defer os
.Remove(name
)
446 run(t
, name
, "./"+name
)
450 // Build a GOPATH package into a shared library that links against the goroot runtime
451 // and an executable that links against both.
452 func TestGopathShlib(t
*testing
.T
) {
453 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "depBase")
454 AssertIsLinkedTo(t
, filepath
.Join(gopathInstallDir
, "libdepBase.so"), soname
)
455 goCmd(t
, "install", "-linkshared", "exe")
456 AssertIsLinkedTo(t
, "./bin/exe", soname
)
457 AssertIsLinkedTo(t
, "./bin/exe", "libdepBase.so")
458 AssertHasRPath(t
, "./bin/exe", gorootInstallDir
)
459 AssertHasRPath(t
, "./bin/exe", gopathInstallDir
)
460 // And check it runs.
461 run(t
, "executable linked to GOPATH library", "./bin/exe")
464 // The shared library contains a note listing the packages it contains in a section
465 // that is not mapped into memory.
466 func testPkgListNote(t
*testing
.T
, f
*elf
.File
, note
*note
) {
467 if note
.section
.Flags
!= 0 {
468 t
.Errorf("package list section has flags %v, want 0", note
.section
.Flags
)
470 if isOffsetLoaded(f
, note
.section
.Offset
) {
471 t
.Errorf("package list section contained in PT_LOAD segment")
473 if note
.desc
!= "depBase\n" {
474 t
.Errorf("incorrect package list %q, want %q", note
.desc
, "depBase\n")
478 // The shared library contains a note containing the ABI hash that is mapped into
479 // memory and there is a local symbol called go.link.abihashbytes that points 16
481 func testABIHashNote(t
*testing
.T
, f
*elf
.File
, note
*note
) {
482 if note
.section
.Flags
!= elf
.SHF_ALLOC
{
483 t
.Errorf("abi hash section has flags %v, want SHF_ALLOC", note
.section
.Flags
)
485 if !isOffsetLoaded(f
, note
.section
.Offset
) {
486 t
.Errorf("abihash section not contained in PT_LOAD segment")
488 var hashbytes elf
.Symbol
489 symbols
, err
:= f
.Symbols()
491 t
.Errorf("error reading symbols %v", err
)
494 for _
, sym
:= range symbols
{
495 if sym
.Name
== "go.link.abihashbytes" {
499 if hashbytes
.Name
== "" {
500 t
.Errorf("no symbol called go.link.abihashbytes")
503 if elf
.ST_BIND(hashbytes
.Info
) != elf
.STB_LOCAL
{
504 t
.Errorf("%s has incorrect binding %v, want STB_LOCAL", hashbytes
.Name
, elf
.ST_BIND(hashbytes
.Info
))
506 if f
.Sections
[hashbytes
.Section
] != note
.section
{
507 t
.Errorf("%s has incorrect section %v, want %s", hashbytes
.Name
, f
.Sections
[hashbytes
.Section
].Name
, note
.section
.Name
)
509 if hashbytes
.Value
-note
.section
.Addr
!= 16 {
510 t
.Errorf("%s has incorrect offset into section %d, want 16", hashbytes
.Name
, hashbytes
.Value
-note
.section
.Addr
)
514 // A Go shared library contains a note indicating which other Go shared libraries it
515 // was linked against in an unmapped section.
516 func testDepsNote(t
*testing
.T
, f
*elf
.File
, note
*note
) {
517 if note
.section
.Flags
!= 0 {
518 t
.Errorf("package list section has flags %v, want 0", note
.section
.Flags
)
520 if isOffsetLoaded(f
, note
.section
.Offset
) {
521 t
.Errorf("package list section contained in PT_LOAD segment")
523 // libdepBase.so just links against the lib containing the runtime.
524 if note
.desc
!= soname
{
525 t
.Errorf("incorrect dependency list %q, want %q", note
.desc
, soname
)
529 // The shared library contains notes with defined contents; see above.
530 func TestNotes(t
*testing
.T
) {
531 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "depBase")
532 f
, err
:= elf
.Open(filepath
.Join(gopathInstallDir
, "libdepBase.so"))
537 notes
, err
:= readNotes(f
)
541 pkgListNoteFound
:= false
542 abiHashNoteFound
:= false
543 depsNoteFound
:= false
544 for _
, note
:= range notes
{
545 if note
.name
!= "Go\x00\x00" {
549 case 1: // ELF_NOTE_GOPKGLIST_TAG
550 if pkgListNoteFound
{
551 t
.Error("multiple package list notes")
553 testPkgListNote(t
, f
, note
)
554 pkgListNoteFound
= true
555 case 2: // ELF_NOTE_GOABIHASH_TAG
556 if abiHashNoteFound
{
557 t
.Error("multiple abi hash notes")
559 testABIHashNote(t
, f
, note
)
560 abiHashNoteFound
= true
561 case 3: // ELF_NOTE_GODEPS_TAG
563 t
.Error("multiple depedency list notes")
565 testDepsNote(t
, f
, note
)
569 if !pkgListNoteFound
{
570 t
.Error("package list note not found")
572 if !abiHashNoteFound
{
573 t
.Error("abi hash note not found")
576 t
.Error("deps note not found")
580 // Build a GOPATH package (depBase) into a shared library that links against the goroot
581 // runtime, another package (dep2) that links against the first, and and an
582 // executable that links against dep2.
583 func TestTwoGopathShlibs(t
*testing
.T
) {
584 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "depBase")
585 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "dep2")
586 goCmd(t
, "install", "-linkshared", "exe2")
587 run(t
, "executable linked to GOPATH library", "./bin/exe2")
590 func TestThreeGopathShlibs(t
*testing
.T
) {
591 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "depBase")
592 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "dep2")
593 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "dep3")
594 goCmd(t
, "install", "-linkshared", "exe3")
595 run(t
, "executable linked to GOPATH library", "./bin/exe3")
598 // If gccgo is not available or not new enough call t.Skip. Otherwise,
599 // return a build.Context that is set up for gccgo.
600 func prepGccgo(t
*testing
.T
) build
.Context
{
601 gccgoName
:= os
.Getenv("GCCGO")
605 gccgoPath
, err
:= exec
.LookPath(gccgoName
)
607 t
.Skip("gccgo not found")
609 cmd
:= exec
.Command(gccgoPath
, "-dumpversion")
610 output
, err
:= cmd
.CombinedOutput()
612 t
.Fatalf("%s -dumpversion failed: %v\n%s", gccgoPath
, err
, output
)
614 if string(output
) < "5" {
615 t
.Skipf("gccgo too old (%s)", strings
.TrimSpace(string(output
)))
617 gccgoContext
:= build
.Default
618 gccgoContext
.InstallSuffix
= suffix
+ "_fPIC"
619 gccgoContext
.Compiler
= "gccgo"
620 gccgoContext
.GOPATH
= os
.Getenv("GOPATH")
624 // Build a GOPATH package into a shared library with gccgo and an executable that
626 func TestGoPathShlibGccgo(t
*testing
.T
) {
627 gccgoContext
:= prepGccgo(t
)
629 libgoRE
:= regexp
.MustCompile("libgo.so.[0-9]+")
631 depP
, err
:= gccgoContext
.Import("depBase", ".", build
.ImportComment
)
633 t
.Fatalf("import failed: %v", err
)
635 gccgoInstallDir
:= filepath
.Join(depP
.PkgTargetRoot
, "shlibs")
636 goCmd(t
, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "depBase")
637 AssertIsLinkedToRegexp(t
, filepath
.Join(gccgoInstallDir
, "libdepBase.so"), libgoRE
)
638 goCmd(t
, "install", "-compiler=gccgo", "-linkshared", "exe")
639 AssertIsLinkedToRegexp(t
, "./bin/exe", libgoRE
)
640 AssertIsLinkedTo(t
, "./bin/exe", "libdepBase.so")
641 AssertHasRPath(t
, "./bin/exe", gccgoInstallDir
)
642 // And check it runs.
643 run(t
, "gccgo-built", "./bin/exe")
646 // The gccgo version of TestTwoGopathShlibs: build a GOPATH package into a shared
647 // library with gccgo, another GOPATH package that depends on the first and an
648 // executable that links the second library.
649 func TestTwoGopathShlibsGccgo(t
*testing
.T
) {
650 gccgoContext
:= prepGccgo(t
)
652 libgoRE
:= regexp
.MustCompile("libgo.so.[0-9]+")
654 depP
, err
:= gccgoContext
.Import("depBase", ".", build
.ImportComment
)
656 t
.Fatalf("import failed: %v", err
)
658 gccgoInstallDir
:= filepath
.Join(depP
.PkgTargetRoot
, "shlibs")
659 goCmd(t
, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "depBase")
660 goCmd(t
, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "dep2")
661 goCmd(t
, "install", "-compiler=gccgo", "-linkshared", "exe2")
663 AssertIsLinkedToRegexp(t
, filepath
.Join(gccgoInstallDir
, "libdepBase.so"), libgoRE
)
664 AssertIsLinkedToRegexp(t
, filepath
.Join(gccgoInstallDir
, "libdep2.so"), libgoRE
)
665 AssertIsLinkedTo(t
, filepath
.Join(gccgoInstallDir
, "libdep2.so"), "libdepBase.so")
666 AssertIsLinkedToRegexp(t
, "./bin/exe2", libgoRE
)
667 AssertIsLinkedTo(t
, "./bin/exe2", "libdep2")
668 AssertIsLinkedTo(t
, "./bin/exe2", "libdepBase.so")
670 // And check it runs.
671 run(t
, "gccgo-built", "./bin/exe2")
674 // Testing rebuilding of shared libraries when they are stale is a bit more
675 // complicated that it seems like it should be. First, we make everything "old": but
676 // only a few seconds old, or it might be older than gc (or the runtime source) and
677 // everything will get rebuilt. Then define a timestamp slightly newer than this
678 // time, which is what we set the mtime to of a file to cause it to be seen as new,
679 // and finally another slightly even newer one that we can compare files against to
680 // see if they have been rebuilt.
681 var oldTime
= time
.Now().Add(-9 * time
.Second
)
682 var nearlyNew
= time
.Now().Add(-6 * time
.Second
)
683 var stampTime
= time
.Now().Add(-3 * time
.Second
)
685 // resetFileStamps makes "everything" (bin, src, pkg from GOPATH and the
686 // test-specific parts of GOROOT) appear old.
687 func resetFileStamps() {
688 chtime
:= func(path
string, info os
.FileInfo
, err error
) error
{
689 return os
.Chtimes(path
, oldTime
, oldTime
)
691 reset
:= func(path
string) {
692 if err
:= filepath
.Walk(path
, chtime
); err
!= nil {
693 log
.Fatalf("resetFileStamps failed: %v", err
)
700 reset(gorootInstallDir
)
703 // touch changes path and returns a function that changes it back.
704 // It also sets the time of the file, so that we can see if it is rewritten.
705 func touch(t
*testing
.T
, path
string) (cleanup
func()) {
706 data
, err
:= ioutil
.ReadFile(path
)
710 old
:= make([]byte, len(data
))
712 if bytes
.HasPrefix(data
, []byte("!<arch>\n")) {
713 // Change last digit of build ID.
714 // (Content ID in the new content-based build IDs.)
715 const marker
= `build id "`
716 i
:= bytes
.Index(data
, []byte(marker
))
718 t
.Fatal("cannot find build id in archive")
720 j
:= bytes
.IndexByte(data
[i
+len(marker
):], '"')
722 t
.Fatal("cannot find build id in archive")
724 i
+= len(marker
) + j
- 1
731 // assume it's a text file
732 data
= append(data
, '\n')
734 if err
:= ioutil
.WriteFile(path
, data
, 0666); err
!= nil {
737 if err
:= os
.Chtimes(path
, nearlyNew
, nearlyNew
); err
!= nil {
741 if err
:= ioutil
.WriteFile(path
, old
, 0666); err
!= nil {
747 // isNew returns if the path is newer than the time stamp used by touch.
748 func isNew(t
*testing
.T
, path
string) bool {
749 fi
, err
:= os
.Stat(path
)
753 return fi
.ModTime().After(stampTime
)
756 // Fail unless path has been rebuilt (i.e. is newer than the time stamp used by
758 func AssertRebuilt(t
*testing
.T
, msg
, path
string) {
761 t
.Errorf("%s was not rebuilt (%s)", msg
, path
)
765 // Fail if path has been rebuilt (i.e. is newer than the time stamp used by isNew)
766 func AssertNotRebuilt(t
*testing
.T
, msg
, path
string) {
769 t
.Errorf("%s was rebuilt (%s)", msg
, path
)
773 func TestRebuilding(t
*testing
.T
) {
774 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "depBase")
775 goCmd(t
, "install", "-linkshared", "exe")
777 // If the source is newer than both the .a file and the .so, both are rebuilt.
778 t
.Run("newsource", func(t
*testing
.T
) {
780 cleanup
:= touch(t
, "src/depBase/dep.go")
783 goCmd(t
, "install", "-linkshared", "exe")
785 goCmd(t
, "install", "-linkshared", "exe")
786 AssertRebuilt(t
, "new source", filepath
.Join(gopathInstallDir
, "depBase.a"))
787 AssertRebuilt(t
, "new source", filepath
.Join(gopathInstallDir
, "libdepBase.so"))
790 // If the .a file is newer than the .so, the .so is rebuilt (but not the .a)
791 t
.Run("newarchive", func(t
*testing
.T
) {
793 AssertNotRebuilt(t
, "new .a file before build", filepath
.Join(gopathInstallDir
, "depBase.a"))
794 goCmd(t
, "list", "-linkshared", "-f={{.ImportPath}} {{.Stale}} {{.StaleReason}} {{.Target}}", "depBase")
795 AssertNotRebuilt(t
, "new .a file before build", filepath
.Join(gopathInstallDir
, "depBase.a"))
796 cleanup
:= touch(t
, filepath
.Join(gopathInstallDir
, "depBase.a"))
799 goCmd(t
, "install", "-v", "-linkshared", "exe")
801 goCmd(t
, "install", "-v", "-linkshared", "exe")
802 AssertNotRebuilt(t
, "new .a file", filepath
.Join(gopathInstallDir
, "depBase.a"))
803 AssertRebuilt(t
, "new .a file", filepath
.Join(gopathInstallDir
, "libdepBase.so"))
807 func appendFile(t
*testing
.T
, path
, content
string) {
808 f
, err
:= os
.OpenFile(path
, os
.O_WRONLY|os
.O_APPEND
, 0660)
810 t
.Fatalf("os.OpenFile failed: %v", err
)
815 t
.Fatalf("f.Close failed: %v", err
)
818 _
, err
= f
.WriteString(content
)
820 t
.Fatalf("f.WriteString failed: %v", err
)
824 func writeFile(t
*testing
.T
, path
, content
string) {
825 err
:= ioutil
.WriteFile(path
, []byte(content
), 0644)
827 t
.Fatalf("ioutil.WriteFile failed: %v", err
)
831 func TestABIChecking(t
*testing
.T
) {
832 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "depBase")
833 goCmd(t
, "install", "-linkshared", "exe")
835 // If we make an ABI-breaking change to depBase and rebuild libp.so but not exe,
836 // exe will abort with a complaint on startup.
837 // This assumes adding an exported function breaks ABI, which is not true in
838 // some senses but suffices for the narrow definition of ABI compatibility the
839 // toolchain uses today.
841 appendFile(t
, "src/depBase/dep.go", "func ABIBreak() {}\n")
842 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "depBase")
843 c
:= exec
.Command("./bin/exe")
844 output
, err
:= c
.CombinedOutput()
846 t
.Fatal("executing exe did not fail after ABI break")
848 scanner
:= bufio
.NewScanner(bytes
.NewReader(output
))
850 const wantLine
= "abi mismatch detected between the executable and libdepBase.so"
852 if scanner
.Text() == wantLine
{
857 if err
= scanner
.Err(); err
!= nil {
858 t
.Errorf("scanner encountered error: %v", err
)
861 t
.Fatalf("exe failed, but without line %q; got output:\n%s", wantLine
, output
)
864 // Rebuilding exe makes it work again.
865 goCmd(t
, "install", "-linkshared", "exe")
866 run(t
, "rebuilt exe", "./bin/exe")
868 // If we make a change which does not break ABI (such as adding an unexported
869 // function) and rebuild libdepBase.so, exe still works, even if new function
870 // is in a file by itself.
872 writeFile(t
, "src/depBase/dep2.go", "package depBase\nfunc noABIBreak() {}\n")
873 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "depBase")
874 run(t
, "after non-ABI breaking change", "./bin/exe")
877 // If a package 'explicit' imports a package 'implicit', building
878 // 'explicit' into a shared library implicitly includes implicit in
879 // the shared library. Building an executable that imports both
880 // explicit and implicit builds the code from implicit into the
881 // executable rather than fetching it from the shared library. The
882 // link still succeeds and the executable still runs though.
883 func TestImplicitInclusion(t
*testing
.T
) {
884 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "explicit")
885 goCmd(t
, "install", "-linkshared", "implicitcmd")
886 run(t
, "running executable linked against library that contains same package as it", "./bin/implicitcmd")
889 // Tests to make sure that the type fields of empty interfaces and itab
890 // fields of nonempty interfaces are unique even across modules,
891 // so that interface equality works correctly.
892 func TestInterface(t
*testing
.T
) {
893 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "iface_a")
894 // Note: iface_i gets installed implicitly as a dependency of iface_a.
895 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "iface_b")
896 goCmd(t
, "install", "-linkshared", "iface")
897 run(t
, "running type/itab uniqueness tester", "./bin/iface")
900 // Access a global variable from a library.
901 func TestGlobal(t
*testing
.T
) {
902 goCmd(t
, "install", "-buildmode=shared", "-linkshared", "globallib")
903 goCmd(t
, "install", "-linkshared", "global")
904 run(t
, "global executable", "./bin/global")
905 AssertIsLinkedTo(t
, "./bin/global", soname
)
906 AssertHasRPath(t
, "./bin/global", gorootInstallDir
)