...

Source file src/cmd/cgo/internal/testshared/shared_test.go

Documentation: cmd/cgo/internal/testshared

     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.
     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  // This is the smallest set of packages we can link into a shared
    38  // library (runtime/cgo is built implicitly).
    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  // run runs a command and calls t.Errorf if it fails.
    46  func run(t *testing.T, msg string, args ...string) {
    47  	runWithEnv(t, msg, nil, args...)
    48  }
    49  
    50  // runWithEnv runs a command under the given environment and calls t.Errorf if it fails.
    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  // goCmd invokes the go tool with the installsuffix set up by TestMain. It calls
    62  // t.Fatalf if the command fails.
    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  			// Panic instead of using log.Fatalf so that deferred cleanup may run in testMain.
    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  // TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit).
    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  	// -buildmode=shared fundamentally does not work in module mode.
   130  	// (It tries to share package dependencies across builds, but in module mode
   131  	// each module has its own distinct set of dependency versions.)
   132  	// We would like to eliminate it (see https://go.dev/issue/47788),
   133  	// but first need to figure out a replacement that covers the small subset
   134  	// of use-cases where -buildmode=shared still works today.
   135  	// For now, run the tests in GOPATH mode only.
   136  	os.Setenv("GO111MODULE", "off")
   137  
   138  	// Some tests need to edit the source in GOPATH, so copy this directory to a
   139  	// temporary directory and chdir to that.
   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  	// Explicitly override GOBIN as well, in case it was set through a GOENV file.
   151  	os.Setenv("GOBIN", filepath.Join(gopath, "bin"))
   152  	os.Chdir(modRoot)
   153  	os.Setenv("PWD", modRoot)
   154  
   155  	// The test also needs to install libraries into GOROOT/pkg, so copy the
   156  	// subset of GOROOT that we need.
   157  	//
   158  	// TODO(golang.org/issue/28553): Rework -buildmode=shared so that it does not
   159  	// need to write to GOROOT.
   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  	// All tests depend on runtime being built into a shared library. Because
   174  	// that takes a few seconds, do it here and have all tests use the version
   175  	// built here.
   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  // cloneTestdataModule clones the packages from src/testshared into gopath.
   208  // It returns the directory within gopath at which the module root is located.
   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  // cloneGOROOTDeps copies (or symlinks) the portions of GOROOT/src and
   221  // GOROOT/pkg relevant to this test into the given directory.
   222  // It must be run from within the testdata module.
   223  func cloneGOROOTDeps(goroot string) error {
   224  	// Before we clone GOROOT, figure out which packages we need to copy over.
   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  				// We will copy in the source for pkg when we copy in prev.
   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  // The shared library was built at the expected location.
   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  // The shared library does not have relocations against the text segment.
   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  // The shared library does not contain symbols called ".dup"
   321  // (See golang.org/issue/14841.)
   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  // The install command should have created a "shlibname" file for the
   343  // listed packages (and runtime/cgo, and math on arm) indicating the
   344  // name of the shared library containing it.
   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  // Is a given offset into the file contained in a loaded segment?
   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  // Read all notes from f. As ELF section names are not supposed to be special, one
   409  // looks for a particular note by scanning all SHT_NOTE sections looking for a note
   410  // with a particular "name" and "tag".
   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, &note{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  // Build a trivial program that links against the shared runtime and check it runs.
   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  	// It is 19K on linux/amd64, with separate-code in binutils ld and 64k being most common alignment
   500  	// 4*64k should be enough, but this might need revision eventually.
   501  	checkSize(t, "../../bin/trivial", 256000)
   502  }
   503  
   504  // Build a trivial program in PIE mode that links against the shared runtime and check it runs.
   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  	// It is 19K on linux/amd64, with separate-code in binutils ld and 64k being most common alignment
   512  	// 4*64k should be enough, but this might need revision eventually.
   513  	checkSize(t, "./trivial.pie", 256000)
   514  }
   515  
   516  // Check that the file size does not exceed a limit.
   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  // Build a division test program and check it runs.
   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  // Build an executable that uses cgo linked against the shared runtime and check it
   535  // runs.
   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  // Build a GOPATH package into a shared library that links against the goroot runtime
   581  // and an executable that links against both.
   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  	// And check it runs.
   593  	run(t, "executable linked to GOPATH library", "../../bin/exe")
   594  }
   595  
   596  // The shared library contains a note listing the packages it contains in a section
   597  // that is not mapped into memory.
   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  // The shared library contains a note containing the ABI hash that is mapped into
   611  // memory and there is a local symbol called go.link.abihashbytes that points 16
   612  // bytes into it.
   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  // A Go shared library contains a note indicating which other Go shared libraries it
   647  // was linked against in an unmapped section.
   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  	// libdepBase.so just links against the lib containing the runtime.
   656  	if note.desc != soname {
   657  		t.Errorf("incorrect dependency list %q, want %q", note.desc, soname)
   658  	}
   659  }
   660  
   661  // The shared library contains notes with defined contents; see above.
   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: // ELF_NOTE_GOPKGLIST_TAG
   684  			if pkgListNoteFound {
   685  				t.Error("multiple package list notes")
   686  			}
   687  			testPkgListNote(t, f, note)
   688  			pkgListNoteFound = true
   689  		case 2: // ELF_NOTE_GOABIHASH_TAG
   690  			if abiHashNoteFound {
   691  				t.Error("multiple abi hash notes")
   692  			}
   693  			testABIHashNote(t, f, note)
   694  			abiHashNoteFound = true
   695  		case 3: // ELF_NOTE_GODEPS_TAG
   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  // Build a GOPATH package (depBase) into a shared library that links against the goroot
   715  // runtime, another package (dep2) that links against the first, and an
   716  // executable that links against dep2.
   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  // If gccgo is not available or not new enough, call t.Skip.
   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  // Build a GOPATH package into a shared library with gccgo and an executable that
   777  // links against it.
   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  	// Run 'go list' after 'go install': with gccgo, we apparently don't know the
   787  	// shlib location until after we've installed it.
   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  	// And check it runs.
   796  	run(t, "gccgo-built", "../../bin/exe")
   797  }
   798  
   799  // The gccgo version of TestTwoGopathShlibs: build a GOPATH package into a shared
   800  // library with gccgo, another GOPATH package that depends on the first and an
   801  // executable that links the second library.
   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  	// Run 'go list' after 'go install': with gccgo, we apparently don't know the
   813  	// shlib location until after we've installed it.
   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  	// And check it runs.
   825  	run(t, "gccgo-built", "../../bin/exe2")
   826  }
   827  
   828  // Testing rebuilding of shared libraries when they are stale is a bit more
   829  // complicated that it seems like it should be. First, we make everything "old": but
   830  // only a few seconds old, or it might be older than gc (or the runtime source) and
   831  // everything will get rebuilt. Then define a timestamp slightly newer than this
   832  // time, which is what we set the mtime to of a file to cause it to be seen as new,
   833  // and finally another slightly even newer one that we can compare files against to
   834  // see if they have been rebuilt.
   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  // resetFileStamps makes "everything" (bin, src, pkg from GOPATH and the
   840  // test-specific parts of GOROOT) appear old.
   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  // touch changes path and returns a function that changes it back.
   858  // It also sets the time of the file, so that we can see if it is rewritten.
   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  		// Change last digit of build ID.
   869  		// (Content ID in the new content-based build IDs.)
   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  		// assume it's a text file
   887  		data = append(data, '\n')
   888  	}
   889  
   890  	// If the file is still a symlink from an overlay, delete it so that we will
   891  	// replace it with a regular file instead of overwriting the symlinked one.
   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  	// If we're replacing a symlink to a read-only file, make the new file
   904  	// user-writable.
   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  // isNew returns if the path is newer than the time stamp used by touch.
   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  // Fail unless path has been rebuilt (i.e. is newer than the time stamp used by
   931  // isNew)
   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  // Fail if path has been rebuilt (i.e. is newer than the time stamp used by isNew)
   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  	// If the source is newer than both the .a file and the .so, both are rebuilt.
   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  	// If the .a file is newer than the .so, the .so is rebuilt (but not the .a)
   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  	// If we make an ABI-breaking change to depBase and rebuild libp.so but not exe,
  1027  	// exe will abort with a complaint on startup.
  1028  	// This assumes adding an exported function breaks ABI, which is not true in
  1029  	// some senses but suffices for the narrow definition of ABI compatibility the
  1030  	// toolchain uses today.
  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  	// Rebuilding exe makes it work again.
  1059  	goCmd(t, "install", "-linkshared", "./exe")
  1060  	run(t, "rebuilt exe", "../../bin/exe")
  1061  
  1062  	// If we make a change which does not break ABI (such as adding an unexported
  1063  	// function) and rebuild libdepBase.so, exe still works, even if new function
  1064  	// is in a file by itself.
  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  // If a package 'explicit' imports a package 'implicit', building
  1072  // 'explicit' into a shared library implicitly includes implicit in
  1073  // the shared library. Building an executable that imports both
  1074  // explicit and implicit builds the code from implicit into the
  1075  // executable rather than fetching it from the shared library. The
  1076  // link still succeeds and the executable still runs though.
  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  // Tests to make sure that the type fields of empty interfaces and itab
  1085  // fields of nonempty interfaces are unique even across modules,
  1086  // so that interface equality works correctly.
  1087  func TestInterface(t *testing.T) {
  1088  	globalSkip(t)
  1089  	goCmd(t, "install", "-buildmode=shared", "-linkshared", "./iface_a")
  1090  	// Note: iface_i gets installed implicitly as a dependency of iface_a.
  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  // Access a global variable from a library.
  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  // Run a test using -linkshared of an installed shared package.
  1107  // Issue 26400.
  1108  func TestTestInstalledShared(t *testing.T) {
  1109  	globalSkip(t)
  1110  	goCmd(t, "test", "-linkshared", "-test.short", "sync/atomic")
  1111  }
  1112  
  1113  // Test generated pointer method with -linkshared.
  1114  // Issue 25065.
  1115  func TestGeneratedMethod(t *testing.T) {
  1116  	globalSkip(t)
  1117  	goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue25065")
  1118  }
  1119  
  1120  // Test use of shared library struct with generated hash function.
  1121  // Issue 30768.
  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  // Test that packages can be added not in dependency order (here a depends on b, and a adds
  1129  // before b). This could happen with e.g. go build -buildmode=shared std. See issue 39777.
  1130  func TestPackageOrder(t *testing.T) {
  1131  	globalSkip(t)
  1132  	goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue39777/a", "./issue39777/b")
  1133  }
  1134  
  1135  // Test that GC data are generated correctly by the linker when it needs a type defined in
  1136  // a shared library. See issue 39927.
  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  // Test that we don't decode type symbols from shared libraries (which has no data,
  1145  // causing panic). See issue 44031.
  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  // Test that we use a variable from shared libraries (which implement an
  1154  // interface in shared libraries.). A weak reference is used in the itab
  1155  // in main process. It can cause unreachable panic. See issue 47873.
  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  // Test that we can build std in shared mode.
  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  	// Use a temporary pkgdir to not interfere with other tests, and not write to GOROOT.
  1177  	// Cannot use goCmd as it runs with cloned GOROOT which is incomplete.
  1178  	runWithEnv(t, "building std", []string{"GOROOT=" + oldGOROOT},
  1179  		filepath.Join(oldGOROOT, "bin", "go"), "install", "-buildmode=shared", "-pkgdir="+tmpDir, "std")
  1180  
  1181  	// Issue #58966.
  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