Source file src/cmd/link/link_test.go

     1  // Copyright 2016 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 main
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"debug/macho"
    11  	"errors"
    12  	"internal/platform"
    13  	"internal/testenv"
    14  	"os"
    15  	"os/exec"
    16  	"path/filepath"
    17  	"regexp"
    18  	"runtime"
    19  	"strings"
    20  	"testing"
    21  
    22  	imacho "cmd/internal/macho"
    23  	"cmd/internal/sys"
    24  )
    25  
    26  var AuthorPaidByTheColumnInch struct {
    27  	fog int `text:"London. Michaelmas term lately over, and the Lord Chancellor sitting in Lincoln’s Inn Hall. Implacable November weather. As much mud in the streets as if the waters had but newly retired from the face of the earth, and it would not be wonderful to meet a Megalosaurus, forty feet long or so, waddling like an elephantine lizard up Holborn Hill. Smoke lowering down from chimney-pots, making a soft black drizzle, with flakes of soot in it as big as full-grown snowflakes—gone into mourning, one might imagine, for the death of the sun. Dogs, undistinguishable in mire. Horses, scarcely better; splashed to their very blinkers. Foot passengers, jostling one another’s umbrellas in a general infection of ill temper, and losing their foot-hold at street-corners, where tens of thousands of other foot passengers have been slipping and sliding since the day broke (if this day ever broke), adding new deposits to the crust upon crust of mud, sticking at those points tenaciously to the pavement, and accumulating at compound interest.  	Fog everywhere. Fog up the river, where it flows among green aits and meadows; fog down the river, where it rolls defiled among the tiers of shipping and the waterside pollutions of a great (and dirty) city. Fog on the Essex marshes, fog on the Kentish heights. Fog creeping into the cabooses of collier-brigs; fog lying out on the yards and hovering in the rigging of great ships; fog drooping on the gunwales of barges and small boats. Fog in the eyes and throats of ancient Greenwich pensioners, wheezing by the firesides of their wards; fog in the stem and bowl of the afternoon pipe of the wrathful skipper, down in his close cabin; fog cruelly pinching the toes and fingers of his shivering little ‘prentice boy on deck. Chance people on the bridges peeping over the parapets into a nether sky of fog, with fog all round them, as if they were up in a balloon and hanging in the misty clouds.  	Gas looming through the fog in divers places in the streets, much as the sun may, from the spongey fields, be seen to loom by husbandman and ploughboy. Most of the shops lighted two hours before their time—as the gas seems to know, for it has a haggard and unwilling look.  	The raw afternoon is rawest, and the dense fog is densest, and the muddy streets are muddiest near that leaden-headed old obstruction, appropriate ornament for the threshold of a leaden-headed old corporation, Temple Bar. And hard by Temple Bar, in Lincoln’s Inn Hall, at the very heart of the fog, sits the Lord High Chancellor in his High Court of Chancery."`
    28  
    29  	wind int `text:"It was grand to see how the wind awoke, and bent the trees, and drove the rain before it like a cloud of smoke; and to hear the solemn thunder, and to see the lightning; and while thinking with awe of the tremendous powers by which our little lives are encompassed, to consider how beneficent they are, and how upon the smallest flower and leaf there was already a freshness poured from all this seeming rage, which seemed to make creation new again."`
    30  
    31  	jarndyce int `text:"Jarndyce and Jarndyce drones on. This scarecrow of a suit has, over the course of time, become so complicated, that no man alive knows what it means. The parties to it understand it least; but it has been observed that no two Chancery lawyers can talk about it for five minutes, without coming to a total disagreement as to all the premises. Innumerable children have been born into the cause; innumerable young people have married into it; innumerable old people have died out of it. Scores of persons have deliriously found themselves made parties in Jarndyce and Jarndyce, without knowing how or why; whole families have inherited legendary hatreds with the suit. The little plaintiff or defendant, who was promised a new rocking-horse when Jarndyce and Jarndyce should be settled, has grown up, possessed himself of a real horse, and trotted away into the other world. Fair wards of court have faded into mothers and grandmothers; a long procession of Chancellors has come in and gone out; the legion of bills in the suit have been transformed into mere bills of mortality; there are not three Jarndyces left upon the earth perhaps, since old Tom Jarndyce in despair blew his brains out at a coffee-house in Chancery Lane; but Jarndyce and Jarndyce still drags its dreary length before the Court, perennially hopeless."`
    32  
    33  	principle int `text:"The one great principle of the English law is, to make business for itself. There is no other principle distinctly, certainly, and consistently maintained through all its narrow turnings. Viewed by this light it becomes a coherent scheme, and not the monstrous maze the laity are apt to think it. Let them but once clearly perceive that its grand principle is to make business for itself at their expense, and surely they will cease to grumble."`
    34  }
    35  
    36  func TestLargeSymName(t *testing.T) {
    37  	// The compiler generates a symbol name using the string form of the
    38  	// type. This tests that the linker can read symbol names larger than
    39  	// the bufio buffer. Issue #15104.
    40  	_ = AuthorPaidByTheColumnInch
    41  }
    42  
    43  func TestIssue21703(t *testing.T) {
    44  	t.Parallel()
    45  
    46  	testenv.MustHaveGoBuild(t)
    47  	testenv.MustInternalLink(t, false)
    48  
    49  	const source = `
    50  package main
    51  const X = "\n!\n"
    52  func main() {}
    53  `
    54  
    55  	tmpdir := t.TempDir()
    56  	main := filepath.Join(tmpdir, "main.go")
    57  
    58  	err := os.WriteFile(main, []byte(source), 0666)
    59  	if err != nil {
    60  		t.Fatalf("failed to write main.go: %v\n", err)
    61  	}
    62  
    63  	importcfgfile := filepath.Join(tmpdir, "importcfg")
    64  	testenv.WriteImportcfg(t, importcfgfile, nil, main)
    65  
    66  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
    67  	cmd.Dir = tmpdir
    68  	out, err := cmd.CombinedOutput()
    69  	if err != nil {
    70  		t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out)
    71  	}
    72  
    73  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "main.o")
    74  	cmd.Dir = tmpdir
    75  	out, err = cmd.CombinedOutput()
    76  	if err != nil {
    77  		if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
    78  			testenv.SkipFlaky(t, 58806)
    79  		}
    80  		t.Fatalf("failed to link main.o: %v, output: %s\n", err, out)
    81  	}
    82  }
    83  
    84  // TestIssue28429 ensures that the linker does not attempt to link
    85  // sections not named *.o. Such sections may be used by a build system
    86  // to, for example, save facts produced by a modular static analysis
    87  // such as golang.org/x/tools/go/analysis.
    88  func TestIssue28429(t *testing.T) {
    89  	t.Parallel()
    90  
    91  	testenv.MustHaveGoBuild(t)
    92  	testenv.MustInternalLink(t, false)
    93  
    94  	tmpdir := t.TempDir()
    95  
    96  	write := func(name, content string) {
    97  		err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
    98  		if err != nil {
    99  			t.Fatal(err)
   100  		}
   101  	}
   102  
   103  	runGo := func(args ...string) {
   104  		cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
   105  		cmd.Dir = tmpdir
   106  		out, err := cmd.CombinedOutput()
   107  		if err != nil {
   108  			if len(args) >= 2 && args[1] == "link" && runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
   109  				testenv.SkipFlaky(t, 58806)
   110  			}
   111  			t.Fatalf("'go %s' failed: %v, output: %s",
   112  				strings.Join(args, " "), err, out)
   113  		}
   114  	}
   115  
   116  	// Compile a main package.
   117  	write("main.go", "package main; func main() {}")
   118  	importcfgfile := filepath.Join(tmpdir, "importcfg")
   119  	testenv.WriteImportcfg(t, importcfgfile, nil, filepath.Join(tmpdir, "main.go"))
   120  	runGo("tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
   121  	runGo("tool", "pack", "c", "main.a", "main.o")
   122  
   123  	// Add an extra section with a short, non-.o name.
   124  	// This simulates an alternative build system.
   125  	write(".facts", "this is not an object file")
   126  	runGo("tool", "pack", "r", "main.a", ".facts")
   127  
   128  	// Verify that the linker does not attempt
   129  	// to compile the extra section.
   130  	runGo("tool", "link", "-importcfg="+importcfgfile, "main.a")
   131  }
   132  
   133  func TestUnresolved(t *testing.T) {
   134  	testenv.MustHaveGoBuild(t)
   135  
   136  	t.Parallel()
   137  
   138  	tmpdir := t.TempDir()
   139  
   140  	write := func(name, content string) {
   141  		err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
   142  		if err != nil {
   143  			t.Fatal(err)
   144  		}
   145  	}
   146  
   147  	// Test various undefined references. Because of issue #29852,
   148  	// this used to give confusing error messages because the
   149  	// linker would find an undefined reference to "zero" created
   150  	// by the runtime package.
   151  
   152  	write("go.mod", "module testunresolved\n")
   153  	write("main.go", `package main
   154  
   155  func main() {
   156          x()
   157  }
   158  
   159  func x()
   160  `)
   161  	write("main.s", `
   162  TEXT ·x(SB),0,$0
   163          MOVD zero<>(SB), AX
   164          MOVD zero(SB), AX
   165          MOVD ·zero(SB), AX
   166          RET
   167  `)
   168  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build")
   169  	cmd.Dir = tmpdir
   170  	cmd.Env = append(os.Environ(),
   171  		"GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath"))
   172  	out, err := cmd.CombinedOutput()
   173  	if err == nil {
   174  		t.Fatalf("expected build to fail, but it succeeded")
   175  	}
   176  	out = regexp.MustCompile("(?m)^#.*\n").ReplaceAll(out, nil)
   177  	got := string(out)
   178  	want := `main.x: relocation target zero not defined
   179  main.x: relocation target zero not defined
   180  main.x: relocation target main.zero not defined
   181  `
   182  	if want != got {
   183  		t.Fatalf("want:\n%sgot:\n%s", want, got)
   184  	}
   185  }
   186  
   187  func TestIssue33979(t *testing.T) {
   188  	testenv.MustHaveGoBuild(t)
   189  	testenv.MustHaveCGO(t)
   190  	testenv.MustInternalLink(t, true)
   191  
   192  	t.Parallel()
   193  
   194  	tmpdir := t.TempDir()
   195  
   196  	write := func(name, content string) {
   197  		err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
   198  		if err != nil {
   199  			t.Fatal(err)
   200  		}
   201  	}
   202  
   203  	run := func(name string, args ...string) string {
   204  		cmd := testenv.Command(t, name, args...)
   205  		cmd.Dir = tmpdir
   206  		out, err := cmd.CombinedOutput()
   207  		if err != nil {
   208  			t.Fatalf("'go %s' failed: %v, output: %s", strings.Join(args, " "), err, out)
   209  		}
   210  		return string(out)
   211  	}
   212  	runGo := func(args ...string) string {
   213  		return run(testenv.GoToolPath(t), args...)
   214  	}
   215  
   216  	// Test object with undefined reference that was not generated
   217  	// by Go, resulting in an SXREF symbol being loaded during linking.
   218  	// Because of issue #33979, the SXREF symbol would be found during
   219  	// error reporting, resulting in confusing error messages.
   220  
   221  	write("main.go", `package main
   222  func main() {
   223          x()
   224  }
   225  func x()
   226  `)
   227  	// The following assembly must work on all architectures.
   228  	write("x.s", `
   229  TEXT ·x(SB),0,$0
   230          CALL foo(SB)
   231          RET
   232  `)
   233  	write("x.c", `
   234  void undefined();
   235  
   236  void foo() {
   237          undefined();
   238  }
   239  `)
   240  
   241  	cc := strings.TrimSpace(runGo("env", "CC"))
   242  	cflags := strings.Fields(runGo("env", "GOGCCFLAGS"))
   243  
   244  	importcfgfile := filepath.Join(tmpdir, "importcfg")
   245  	testenv.WriteImportcfg(t, importcfgfile, nil, "runtime")
   246  
   247  	// Compile, assemble and pack the Go and C code.
   248  	runGo("tool", "asm", "-p=main", "-gensymabis", "-o", "symabis", "x.s")
   249  	runGo("tool", "compile", "-importcfg="+importcfgfile, "-symabis", "symabis", "-p=main", "-o", "x1.o", "main.go")
   250  	runGo("tool", "asm", "-p=main", "-o", "x2.o", "x.s")
   251  	run(cc, append(cflags, "-c", "-o", "x3.o", "x.c")...)
   252  	runGo("tool", "pack", "c", "x.a", "x1.o", "x2.o", "x3.o")
   253  
   254  	// Now attempt to link using the internal linker.
   255  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-linkmode=internal", "x.a")
   256  	cmd.Dir = tmpdir
   257  	out, err := cmd.CombinedOutput()
   258  	if err == nil {
   259  		t.Fatalf("expected link to fail, but it succeeded")
   260  	}
   261  	re := regexp.MustCompile(`(?m)^main\(.*text\): relocation target undefined not defined$`)
   262  	if !re.Match(out) {
   263  		t.Fatalf("got:\n%q\nwant:\n%s", out, re)
   264  	}
   265  }
   266  
   267  func TestBuildForTvOS(t *testing.T) {
   268  	testenv.MustHaveCGO(t)
   269  	testenv.MustHaveGoBuild(t)
   270  
   271  	// Only run this on darwin, where we can cross build for tvOS.
   272  	if runtime.GOOS != "darwin" {
   273  		t.Skip("skipping on non-darwin platform")
   274  	}
   275  	if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
   276  		t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty")
   277  	}
   278  	if err := testenv.Command(t, "xcrun", "--help").Run(); err != nil {
   279  		t.Skipf("error running xcrun, required for iOS cross build: %v", err)
   280  	}
   281  
   282  	t.Parallel()
   283  
   284  	sdkPath, err := testenv.Command(t, "xcrun", "--sdk", "appletvos", "--show-sdk-path").Output()
   285  	if err != nil {
   286  		t.Skip("failed to locate appletvos SDK, skipping")
   287  	}
   288  	CC := []string{
   289  		"clang",
   290  		"-arch",
   291  		"arm64",
   292  		"-isysroot", strings.TrimSpace(string(sdkPath)),
   293  		"-mtvos-version-min=12.0",
   294  		"-fembed-bitcode",
   295  	}
   296  	CGO_LDFLAGS := []string{"-framework", "CoreFoundation"}
   297  	lib := filepath.Join("testdata", "testBuildFortvOS", "lib.go")
   298  	tmpDir := t.TempDir()
   299  
   300  	ar := filepath.Join(tmpDir, "lib.a")
   301  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", ar, lib)
   302  	env := []string{
   303  		"CGO_ENABLED=1",
   304  		"GOOS=ios",
   305  		"GOARCH=arm64",
   306  		"CC=" + strings.Join(CC, " "),
   307  		"CGO_CFLAGS=", // ensure CGO_CFLAGS does not contain any flags. Issue #35459
   308  		"CGO_LDFLAGS=" + strings.Join(CGO_LDFLAGS, " "),
   309  	}
   310  	cmd.Env = append(os.Environ(), env...)
   311  	t.Logf("%q %v", env, cmd)
   312  	if out, err := cmd.CombinedOutput(); err != nil {
   313  		t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
   314  	}
   315  
   316  	link := testenv.Command(t, CC[0], CC[1:]...)
   317  	link.Args = append(link.Args, CGO_LDFLAGS...)
   318  	link.Args = append(link.Args, "-o", filepath.Join(tmpDir, "a.out")) // Avoid writing to package directory.
   319  	link.Args = append(link.Args, ar, filepath.Join("testdata", "testBuildFortvOS", "main.m"))
   320  	t.Log(link)
   321  	if out, err := link.CombinedOutput(); err != nil {
   322  		t.Fatalf("%v: %v:\n%s", link.Args, err, out)
   323  	}
   324  }
   325  
   326  var testXFlagSrc = `
   327  package main
   328  var X = "hello"
   329  var Z = [99999]int{99998:12345} // make it large enough to be mmaped
   330  func main() { println(X) }
   331  `
   332  
   333  func TestXFlag(t *testing.T) {
   334  	testenv.MustHaveGoBuild(t)
   335  
   336  	t.Parallel()
   337  
   338  	tmpdir := t.TempDir()
   339  
   340  	src := filepath.Join(tmpdir, "main.go")
   341  	err := os.WriteFile(src, []byte(testXFlagSrc), 0666)
   342  	if err != nil {
   343  		t.Fatal(err)
   344  	}
   345  
   346  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src)
   347  	if out, err := cmd.CombinedOutput(); err != nil {
   348  		t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
   349  	}
   350  }
   351  
   352  var trivialSrc = `
   353  package main
   354  func main() { }
   355  `
   356  
   357  func TestMachOBuildVersion(t *testing.T) {
   358  	testenv.MustHaveGoBuild(t)
   359  
   360  	t.Parallel()
   361  
   362  	tmpdir := t.TempDir()
   363  
   364  	src := filepath.Join(tmpdir, "main.go")
   365  	err := os.WriteFile(src, []byte(trivialSrc), 0666)
   366  	if err != nil {
   367  		t.Fatal(err)
   368  	}
   369  
   370  	exe := filepath.Join(tmpdir, "main")
   371  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal", "-o", exe, src)
   372  	cmd.Env = append(os.Environ(),
   373  		"CGO_ENABLED=0",
   374  		"GOOS=darwin",
   375  		"GOARCH=amd64",
   376  	)
   377  	if out, err := cmd.CombinedOutput(); err != nil {
   378  		t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
   379  	}
   380  	exef, err := os.Open(exe)
   381  	if err != nil {
   382  		t.Fatal(err)
   383  	}
   384  	defer exef.Close()
   385  	exem, err := macho.NewFile(exef)
   386  	if err != nil {
   387  		t.Fatal(err)
   388  	}
   389  	found := false
   390  	checkMin := func(ver uint32) {
   391  		major, minor, patch := (ver>>16)&0xff, (ver>>8)&0xff, (ver>>0)&0xff
   392  		if major < 11 {
   393  			t.Errorf("LC_BUILD_VERSION version %d.%d.%d < 11.0.0", major, minor, patch)
   394  		}
   395  	}
   396  	for _, cmd := range exem.Loads {
   397  		raw := cmd.Raw()
   398  		type_ := exem.ByteOrder.Uint32(raw)
   399  		if type_ != imacho.LC_BUILD_VERSION {
   400  			continue
   401  		}
   402  		osVer := exem.ByteOrder.Uint32(raw[12:])
   403  		checkMin(osVer)
   404  		sdkVer := exem.ByteOrder.Uint32(raw[16:])
   405  		checkMin(sdkVer)
   406  		found = true
   407  		break
   408  	}
   409  	if !found {
   410  		t.Errorf("no LC_BUILD_VERSION load command found")
   411  	}
   412  }
   413  
   414  func TestMachOUUID(t *testing.T) {
   415  	testenv.MustHaveGoBuild(t)
   416  	if runtime.GOOS != "darwin" {
   417  		t.Skip("this is only for darwin")
   418  	}
   419  
   420  	t.Parallel()
   421  
   422  	tmpdir := t.TempDir()
   423  
   424  	src := filepath.Join(tmpdir, "main.go")
   425  	err := os.WriteFile(src, []byte(trivialSrc), 0666)
   426  	if err != nil {
   427  		t.Fatal(err)
   428  	}
   429  
   430  	extractUUID := func(exe string) string {
   431  		exem, err := macho.Open(exe)
   432  		if err != nil {
   433  			t.Fatal(err)
   434  		}
   435  		defer exem.Close()
   436  		for _, cmd := range exem.Loads {
   437  			raw := cmd.Raw()
   438  			type_ := exem.ByteOrder.Uint32(raw)
   439  			if type_ != imacho.LC_UUID {
   440  				continue
   441  			}
   442  			return string(raw[8:24])
   443  		}
   444  		return ""
   445  	}
   446  
   447  	tests := []struct{ name, ldflags, expect string }{
   448  		{"default", "", "gobuildid"},
   449  		{"gobuildid", "-B=gobuildid", "gobuildid"},
   450  		{"specific", "-B=0x0123456789ABCDEF0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
   451  		{"none", "-B=none", ""},
   452  	}
   453  	if testenv.HasCGO() {
   454  		for _, test := range tests {
   455  			t1 := test
   456  			t1.name += "_external"
   457  			t1.ldflags += " -linkmode=external"
   458  			tests = append(tests, t1)
   459  		}
   460  	}
   461  	for _, test := range tests {
   462  		t.Run(test.name, func(t *testing.T) {
   463  			exe := filepath.Join(tmpdir, test.name)
   464  			cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags="+test.ldflags, "-o", exe, src)
   465  			if out, err := cmd.CombinedOutput(); err != nil {
   466  				t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
   467  			}
   468  			uuid := extractUUID(exe)
   469  			if test.expect == "gobuildid" {
   470  				// Go buildid is not known in source code. Check UUID is present,
   471  				// and satisifies UUIDv3.
   472  				if uuid == "" {
   473  					t.Fatal("expect nonempty UUID, got empty")
   474  				}
   475  				// The version number is the high 4 bits of byte 6.
   476  				if uuid[6]>>4 != 3 {
   477  					t.Errorf("expect v3 UUID, got %X (version %d)", uuid, uuid[6]>>4)
   478  				}
   479  			} else if uuid != test.expect {
   480  				t.Errorf("UUID mismatch: got %X, want %X", uuid, test.expect)
   481  			}
   482  		})
   483  	}
   484  }
   485  
   486  const Issue34788src = `
   487  
   488  package blah
   489  
   490  func Blah(i int) int {
   491  	a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
   492  	return a[i&7]
   493  }
   494  `
   495  
   496  func TestIssue34788Android386TLSSequence(t *testing.T) {
   497  	testenv.MustHaveGoBuild(t)
   498  
   499  	// This is a cross-compilation test, so it doesn't make
   500  	// sense to run it on every GOOS/GOARCH combination. Limit
   501  	// the test to amd64 + darwin/linux.
   502  	if runtime.GOARCH != "amd64" ||
   503  		(runtime.GOOS != "darwin" && runtime.GOOS != "linux") {
   504  		t.Skip("skipping on non-{linux,darwin}/amd64 platform")
   505  	}
   506  
   507  	t.Parallel()
   508  
   509  	tmpdir := t.TempDir()
   510  
   511  	src := filepath.Join(tmpdir, "blah.go")
   512  	err := os.WriteFile(src, []byte(Issue34788src), 0666)
   513  	if err != nil {
   514  		t.Fatal(err)
   515  	}
   516  
   517  	obj := filepath.Join(tmpdir, "blah.o")
   518  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-p=blah", "-o", obj, src)
   519  	cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")
   520  	if out, err := cmd.CombinedOutput(); err != nil {
   521  		t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)
   522  	}
   523  
   524  	// Run objdump on the resulting object.
   525  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "objdump", obj)
   526  	out, oerr := cmd.CombinedOutput()
   527  	if oerr != nil {
   528  		t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)
   529  	}
   530  
   531  	// Sift through the output; we should not be seeing any R_TLS_LE relocs.
   532  	scanner := bufio.NewScanner(bytes.NewReader(out))
   533  	for scanner.Scan() {
   534  		line := scanner.Text()
   535  		if strings.Contains(line, "R_TLS_LE") {
   536  			t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)
   537  		}
   538  	}
   539  }
   540  
   541  const testStrictDupGoSrc = `
   542  package main
   543  func f()
   544  func main() { f() }
   545  `
   546  
   547  const testStrictDupAsmSrc1 = `
   548  #include "textflag.h"
   549  TEXT	·f(SB), NOSPLIT|DUPOK, $0-0
   550  	RET
   551  `
   552  
   553  const testStrictDupAsmSrc2 = `
   554  #include "textflag.h"
   555  TEXT	·f(SB), NOSPLIT|DUPOK, $0-0
   556  	JMP	0(PC)
   557  `
   558  
   559  const testStrictDupAsmSrc3 = `
   560  #include "textflag.h"
   561  GLOBL ·rcon(SB), RODATA|DUPOK, $64
   562  `
   563  
   564  const testStrictDupAsmSrc4 = `
   565  #include "textflag.h"
   566  GLOBL ·rcon(SB), RODATA|DUPOK, $32
   567  `
   568  
   569  func TestStrictDup(t *testing.T) {
   570  	// Check that -strictdups flag works.
   571  	testenv.MustHaveGoBuild(t)
   572  
   573  	asmfiles := []struct {
   574  		fname   string
   575  		payload string
   576  	}{
   577  		{"a", testStrictDupAsmSrc1},
   578  		{"b", testStrictDupAsmSrc2},
   579  		{"c", testStrictDupAsmSrc3},
   580  		{"d", testStrictDupAsmSrc4},
   581  	}
   582  
   583  	t.Parallel()
   584  
   585  	tmpdir := t.TempDir()
   586  
   587  	src := filepath.Join(tmpdir, "x.go")
   588  	err := os.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
   589  	if err != nil {
   590  		t.Fatal(err)
   591  	}
   592  	for _, af := range asmfiles {
   593  		src = filepath.Join(tmpdir, af.fname+".s")
   594  		err = os.WriteFile(src, []byte(af.payload), 0666)
   595  		if err != nil {
   596  			t.Fatal(err)
   597  		}
   598  	}
   599  	src = filepath.Join(tmpdir, "go.mod")
   600  	err = os.WriteFile(src, []byte("module teststrictdup\n"), 0666)
   601  	if err != nil {
   602  		t.Fatal(err)
   603  	}
   604  
   605  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1")
   606  	cmd.Dir = tmpdir
   607  	out, err := cmd.CombinedOutput()
   608  	if err != nil {
   609  		t.Errorf("linking with -strictdups=1 failed: %v\n%s", err, string(out))
   610  	}
   611  	if !bytes.Contains(out, []byte("mismatched payload")) {
   612  		t.Errorf("unexpected output:\n%s", out)
   613  	}
   614  
   615  	cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2")
   616  	cmd.Dir = tmpdir
   617  	out, err = cmd.CombinedOutput()
   618  	if err == nil {
   619  		t.Errorf("linking with -strictdups=2 did not fail")
   620  	}
   621  	// NB: on amd64 we get the 'new length' error, on arm64 the 'different
   622  	// contents' error.
   623  	if !(bytes.Contains(out, []byte("mismatched payload: new length")) ||
   624  		bytes.Contains(out, []byte("mismatched payload: same length but different contents"))) ||
   625  		!bytes.Contains(out, []byte("mismatched payload: different sizes")) {
   626  		t.Errorf("unexpected output:\n%s", out)
   627  	}
   628  }
   629  
   630  const testFuncAlignSrc = `
   631  package main
   632  import (
   633  	"fmt"
   634  )
   635  func alignPc()
   636  var alignPcFnAddr uintptr
   637  
   638  func main() {
   639  	if alignPcFnAddr % 512 != 0 {
   640  		fmt.Printf("expected 512 bytes alignment, got %v\n", alignPcFnAddr)
   641  	} else {
   642  		fmt.Printf("PASS")
   643  	}
   644  }
   645  `
   646  
   647  var testFuncAlignAsmSources = map[string]string{
   648  	"arm64": `
   649  #include "textflag.h"
   650  
   651  TEXT	·alignPc(SB),NOSPLIT, $0-0
   652  	MOVD	$2, R0
   653  	PCALIGN	$512
   654  	MOVD	$3, R1
   655  	RET
   656  
   657  GLOBL	·alignPcFnAddr(SB),RODATA,$8
   658  DATA	·alignPcFnAddr(SB)/8,$·alignPc(SB)
   659  `,
   660  	"loong64": `
   661  #include "textflag.h"
   662  
   663  TEXT	·alignPc(SB),NOSPLIT, $0-0
   664  	MOVV	$2, R4
   665  	PCALIGN	$512
   666  	MOVV	$3, R5
   667  	RET
   668  
   669  GLOBL	·alignPcFnAddr(SB),RODATA,$8
   670  DATA	·alignPcFnAddr(SB)/8,$·alignPc(SB)
   671  `,
   672  }
   673  
   674  // TestFuncAlign verifies that the address of a function can be aligned
   675  // with a specific value on arm64 and loong64.
   676  func TestFuncAlign(t *testing.T) {
   677  	testFuncAlignAsmSrc := testFuncAlignAsmSources[runtime.GOARCH]
   678  	if len(testFuncAlignAsmSrc) == 0 || runtime.GOOS != "linux" {
   679  		t.Skip("skipping on non-linux/{arm64,loong64} platform")
   680  	}
   681  	testenv.MustHaveGoBuild(t)
   682  
   683  	t.Parallel()
   684  
   685  	tmpdir := t.TempDir()
   686  
   687  	src := filepath.Join(tmpdir, "go.mod")
   688  	err := os.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666)
   689  	if err != nil {
   690  		t.Fatal(err)
   691  	}
   692  	src = filepath.Join(tmpdir, "falign.go")
   693  	err = os.WriteFile(src, []byte(testFuncAlignSrc), 0666)
   694  	if err != nil {
   695  		t.Fatal(err)
   696  	}
   697  	src = filepath.Join(tmpdir, "falign.s")
   698  	err = os.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)
   699  	if err != nil {
   700  		t.Fatal(err)
   701  	}
   702  
   703  	// Build and run with old object file format.
   704  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "falign")
   705  	cmd.Dir = tmpdir
   706  	out, err := cmd.CombinedOutput()
   707  	if err != nil {
   708  		t.Errorf("build failed: %v", err)
   709  	}
   710  	cmd = testenv.Command(t, tmpdir+"/falign")
   711  	out, err = cmd.CombinedOutput()
   712  	if err != nil {
   713  		t.Errorf("failed to run with err %v, output: %s", err, out)
   714  	}
   715  	if string(out) != "PASS" {
   716  		t.Errorf("unexpected output: %s\n", out)
   717  	}
   718  }
   719  
   720  const testTrampSrc = `
   721  package main
   722  import "fmt"
   723  func main() {
   724  	fmt.Println("hello")
   725  
   726  	defer func(){
   727  		if e := recover(); e == nil {
   728  			panic("did not panic")
   729  		}
   730  	}()
   731  	f1()
   732  }
   733  
   734  // Test deferreturn trampolines. See issue #39049.
   735  func f1() { defer f2() }
   736  func f2() { panic("XXX") }
   737  `
   738  
   739  func TestTrampoline(t *testing.T) {
   740  	// Test that trampoline insertion works as expected.
   741  	// For stress test, we set -debugtramp=2 flag, which sets a very low
   742  	// threshold for trampoline generation, and essentially all cross-package
   743  	// calls will use trampolines.
   744  	buildmodes := []string{"default"}
   745  	switch runtime.GOARCH {
   746  	case "arm", "arm64", "ppc64", "loong64":
   747  	case "ppc64le":
   748  		// Trampolines are generated differently when internal linking PIE, test them too.
   749  		buildmodes = append(buildmodes, "pie")
   750  	default:
   751  		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
   752  	}
   753  
   754  	testenv.MustHaveGoBuild(t)
   755  
   756  	t.Parallel()
   757  
   758  	tmpdir := t.TempDir()
   759  
   760  	src := filepath.Join(tmpdir, "hello.go")
   761  	err := os.WriteFile(src, []byte(testTrampSrc), 0666)
   762  	if err != nil {
   763  		t.Fatal(err)
   764  	}
   765  	exe := filepath.Join(tmpdir, "hello.exe")
   766  
   767  	for _, mode := range buildmodes {
   768  		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
   769  		out, err := cmd.CombinedOutput()
   770  		if err != nil {
   771  			t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
   772  		}
   773  		cmd = testenv.Command(t, exe)
   774  		out, err = cmd.CombinedOutput()
   775  		if err != nil {
   776  			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
   777  		}
   778  		if string(out) != "hello\n" {
   779  			t.Errorf("unexpected output (%s):\n%s", mode, out)
   780  		}
   781  
   782  		out, err = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput()
   783  		if err != nil {
   784  			t.Errorf("nm failure: %s\n%s\n", err, string(out))
   785  		}
   786  		if ok, _ := regexp.Match("T runtime.deferreturn(\\+0)?-tramp0", out); !ok {
   787  			t.Errorf("Trampoline T runtime.deferreturn(+0)?-tramp0 is missing")
   788  		}
   789  	}
   790  }
   791  
   792  const testTrampCgoSrc = `
   793  package main
   794  
   795  // #include <stdio.h>
   796  // void CHello() { printf("hello\n"); fflush(stdout); }
   797  import "C"
   798  
   799  func main() {
   800  	C.CHello()
   801  }
   802  `
   803  
   804  func TestTrampolineCgo(t *testing.T) {
   805  	// Test that trampoline insertion works for cgo code.
   806  	// For stress test, we set -debugtramp=2 flag, which sets a very low
   807  	// threshold for trampoline generation, and essentially all cross-package
   808  	// calls will use trampolines.
   809  	buildmodes := []string{"default"}
   810  	switch runtime.GOARCH {
   811  	case "arm", "arm64", "ppc64", "loong64":
   812  	case "ppc64le":
   813  		// Trampolines are generated differently when internal linking PIE, test them too.
   814  		buildmodes = append(buildmodes, "pie")
   815  	default:
   816  		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
   817  	}
   818  
   819  	testenv.MustHaveGoBuild(t)
   820  	testenv.MustHaveCGO(t)
   821  
   822  	t.Parallel()
   823  
   824  	tmpdir := t.TempDir()
   825  
   826  	src := filepath.Join(tmpdir, "hello.go")
   827  	err := os.WriteFile(src, []byte(testTrampCgoSrc), 0666)
   828  	if err != nil {
   829  		t.Fatal(err)
   830  	}
   831  	exe := filepath.Join(tmpdir, "hello.exe")
   832  
   833  	for _, mode := range buildmodes {
   834  		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
   835  		out, err := cmd.CombinedOutput()
   836  		if err != nil {
   837  			t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
   838  		}
   839  		cmd = testenv.Command(t, exe)
   840  		out, err = cmd.CombinedOutput()
   841  		if err != nil {
   842  			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
   843  		}
   844  		if string(out) != "hello\n" && string(out) != "hello\r\n" {
   845  			t.Errorf("unexpected output (%s):\n%s", mode, out)
   846  		}
   847  
   848  		// Test internal linking mode.
   849  
   850  		if !testenv.CanInternalLink(true) {
   851  			continue
   852  		}
   853  		cmd = testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
   854  		out, err = cmd.CombinedOutput()
   855  		if err != nil {
   856  			t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
   857  		}
   858  		cmd = testenv.Command(t, exe)
   859  		out, err = cmd.CombinedOutput()
   860  		if err != nil {
   861  			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
   862  		}
   863  		if string(out) != "hello\n" && string(out) != "hello\r\n" {
   864  			t.Errorf("unexpected output (%s):\n%s", mode, out)
   865  		}
   866  	}
   867  }
   868  
   869  func TestIndexMismatch(t *testing.T) {
   870  	// Test that index mismatch will cause a link-time error (not run-time error).
   871  	// This shouldn't happen with "go build". We invoke the compiler and the linker
   872  	// manually, and try to "trick" the linker with an inconsistent object file.
   873  	testenv.MustHaveGoBuild(t)
   874  	testenv.MustInternalLink(t, false)
   875  
   876  	t.Parallel()
   877  
   878  	tmpdir := t.TempDir()
   879  
   880  	aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")
   881  	bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")
   882  	mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")
   883  	aObj := filepath.Join(tmpdir, "a.o")
   884  	mObj := filepath.Join(tmpdir, "main.o")
   885  	exe := filepath.Join(tmpdir, "main.exe")
   886  
   887  	importcfgFile := filepath.Join(tmpdir, "runtime.importcfg")
   888  	testenv.WriteImportcfg(t, importcfgFile, nil, "runtime")
   889  	importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg")
   890  	testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj}, "runtime")
   891  
   892  	// Build a program with main package importing package a.
   893  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, aSrc)
   894  	t.Log(cmd)
   895  	out, err := cmd.CombinedOutput()
   896  	if err != nil {
   897  		t.Fatalf("compiling a.go failed: %v\n%s", err, out)
   898  	}
   899  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-I", tmpdir, "-o", mObj, mSrc)
   900  	t.Log(cmd)
   901  	out, err = cmd.CombinedOutput()
   902  	if err != nil {
   903  		t.Fatalf("compiling main.go failed: %v\n%s", err, out)
   904  	}
   905  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
   906  	t.Log(cmd)
   907  	out, err = cmd.CombinedOutput()
   908  	if err != nil {
   909  		if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
   910  			testenv.SkipFlaky(t, 58806)
   911  		}
   912  		t.Errorf("linking failed: %v\n%s", err, out)
   913  	}
   914  
   915  	// Now, overwrite a.o with the object of b.go. This should
   916  	// result in an index mismatch.
   917  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, bSrc)
   918  	t.Log(cmd)
   919  	out, err = cmd.CombinedOutput()
   920  	if err != nil {
   921  		t.Fatalf("compiling a.go failed: %v\n%s", err, out)
   922  	}
   923  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
   924  	t.Log(cmd)
   925  	out, err = cmd.CombinedOutput()
   926  	if err == nil {
   927  		t.Fatalf("linking didn't fail")
   928  	}
   929  	if !bytes.Contains(out, []byte("fingerprint mismatch")) {
   930  		t.Errorf("did not see expected error message. out:\n%s", out)
   931  	}
   932  }
   933  
   934  func TestPErsrcBinutils(t *testing.T) {
   935  	// Test that PE rsrc section is handled correctly (issue 39658).
   936  	testenv.MustHaveGoBuild(t)
   937  
   938  	if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" {
   939  		// This test is limited to amd64 and 386, because binutils is limited as such
   940  		t.Skipf("this is only for windows/amd64 and windows/386")
   941  	}
   942  
   943  	t.Parallel()
   944  
   945  	tmpdir := t.TempDir()
   946  
   947  	pkgdir := filepath.Join("testdata", "pe-binutils")
   948  	exe := filepath.Join(tmpdir, "a.exe")
   949  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe)
   950  	cmd.Dir = pkgdir
   951  	// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
   952  	out, err := cmd.CombinedOutput()
   953  	if err != nil {
   954  		t.Fatalf("building failed: %v, output:\n%s", err, out)
   955  	}
   956  
   957  	// Check that the binary contains the rsrc data
   958  	b, err := os.ReadFile(exe)
   959  	if err != nil {
   960  		t.Fatalf("reading output failed: %v", err)
   961  	}
   962  	if !bytes.Contains(b, []byte("Hello Gophers!")) {
   963  		t.Fatalf("binary does not contain expected content")
   964  	}
   965  }
   966  
   967  func TestPErsrcLLVM(t *testing.T) {
   968  	// Test that PE rsrc section is handled correctly (issue 39658).
   969  	testenv.MustHaveGoBuild(t)
   970  
   971  	if runtime.GOOS != "windows" {
   972  		t.Skipf("this is a windows-only test")
   973  	}
   974  
   975  	t.Parallel()
   976  
   977  	tmpdir := t.TempDir()
   978  
   979  	pkgdir := filepath.Join("testdata", "pe-llvm")
   980  	exe := filepath.Join(tmpdir, "a.exe")
   981  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe)
   982  	cmd.Dir = pkgdir
   983  	// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
   984  	out, err := cmd.CombinedOutput()
   985  	if err != nil {
   986  		t.Fatalf("building failed: %v, output:\n%s", err, out)
   987  	}
   988  
   989  	// Check that the binary contains the rsrc data
   990  	b, err := os.ReadFile(exe)
   991  	if err != nil {
   992  		t.Fatalf("reading output failed: %v", err)
   993  	}
   994  	if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
   995  		t.Fatalf("binary does not contain expected content")
   996  	}
   997  }
   998  
   999  func TestContentAddressableSymbols(t *testing.T) {
  1000  	// Test that the linker handles content-addressable symbols correctly.
  1001  	testenv.MustHaveGoBuild(t)
  1002  
  1003  	t.Parallel()
  1004  
  1005  	src := filepath.Join("testdata", "testHashedSyms", "p.go")
  1006  	cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
  1007  	out, err := cmd.CombinedOutput()
  1008  	if err != nil {
  1009  		t.Errorf("command %s failed: %v\n%s", cmd, err, out)
  1010  	}
  1011  }
  1012  
  1013  func TestReadOnly(t *testing.T) {
  1014  	// Test that read-only data is indeed read-only.
  1015  	testenv.MustHaveGoBuild(t)
  1016  
  1017  	t.Parallel()
  1018  
  1019  	src := filepath.Join("testdata", "testRO", "x.go")
  1020  	cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
  1021  	out, err := cmd.CombinedOutput()
  1022  	if err == nil {
  1023  		t.Errorf("running test program did not fail. output:\n%s", out)
  1024  	}
  1025  }
  1026  
  1027  const testIssue38554Src = `
  1028  package main
  1029  
  1030  type T [10<<20]byte
  1031  
  1032  //go:noinline
  1033  func f() T {
  1034  	return T{} // compiler will make a large stmp symbol, but not used.
  1035  }
  1036  
  1037  func main() {
  1038  	x := f()
  1039  	println(x[1])
  1040  }
  1041  `
  1042  
  1043  func TestIssue38554(t *testing.T) {
  1044  	testenv.MustHaveGoBuild(t)
  1045  
  1046  	t.Parallel()
  1047  
  1048  	tmpdir := t.TempDir()
  1049  
  1050  	src := filepath.Join(tmpdir, "x.go")
  1051  	err := os.WriteFile(src, []byte(testIssue38554Src), 0666)
  1052  	if err != nil {
  1053  		t.Fatalf("failed to write source file: %v", err)
  1054  	}
  1055  	exe := filepath.Join(tmpdir, "x.exe")
  1056  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
  1057  	out, err := cmd.CombinedOutput()
  1058  	if err != nil {
  1059  		t.Fatalf("build failed: %v\n%s", err, out)
  1060  	}
  1061  
  1062  	fi, err := os.Stat(exe)
  1063  	if err != nil {
  1064  		t.Fatalf("failed to stat output file: %v", err)
  1065  	}
  1066  
  1067  	// The test program is not much different from a helloworld, which is
  1068  	// typically a little over 1 MB. We allow 5 MB. If the bad stmp is live,
  1069  	// it will be over 10 MB.
  1070  	const want = 5 << 20
  1071  	if got := fi.Size(); got > want {
  1072  		t.Errorf("binary too big: got %d, want < %d", got, want)
  1073  	}
  1074  }
  1075  
  1076  const testIssue42396src = `
  1077  package main
  1078  
  1079  //go:noinline
  1080  //go:nosplit
  1081  func callee(x int) {
  1082  }
  1083  
  1084  func main() {
  1085  	callee(9)
  1086  }
  1087  `
  1088  
  1089  func TestIssue42396(t *testing.T) {
  1090  	testenv.MustHaveGoBuild(t)
  1091  
  1092  	if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
  1093  		t.Skip("no race detector support")
  1094  	}
  1095  
  1096  	t.Parallel()
  1097  
  1098  	tmpdir := t.TempDir()
  1099  
  1100  	src := filepath.Join(tmpdir, "main.go")
  1101  	err := os.WriteFile(src, []byte(testIssue42396src), 0666)
  1102  	if err != nil {
  1103  		t.Fatalf("failed to write source file: %v", err)
  1104  	}
  1105  	exe := filepath.Join(tmpdir, "main.exe")
  1106  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-gcflags=-race", "-o", exe, src)
  1107  	out, err := cmd.CombinedOutput()
  1108  	if err == nil {
  1109  		t.Fatalf("build unexpectedly succeeded")
  1110  	}
  1111  
  1112  	// Check to make sure that we see a reasonable error message
  1113  	// and not a panic.
  1114  	if strings.Contains(string(out), "panic:") {
  1115  		t.Fatalf("build should not fail with panic:\n%s", out)
  1116  	}
  1117  	const want = "reference to undefined builtin"
  1118  	if !strings.Contains(string(out), want) {
  1119  		t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
  1120  	}
  1121  }
  1122  
  1123  const testLargeRelocSrc = `
  1124  package main
  1125  
  1126  var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
  1127  
  1128  var addr = [...]*byte{
  1129  	&x[1<<23-1],
  1130  	&x[1<<23],
  1131  	&x[1<<23+1],
  1132  	&x[1<<24-1],
  1133  	&x[1<<24],
  1134  	&x[1<<24+1],
  1135  }
  1136  
  1137  func main() {
  1138  	// check relocations in instructions
  1139  	check(x[1<<23-1], 0)
  1140  	check(x[1<<23], 23)
  1141  	check(x[1<<23+1], 0)
  1142  	check(x[1<<24-1], 0)
  1143  	check(x[1<<24], 24)
  1144  	check(x[1<<24+1], 0)
  1145  
  1146  	// check absolute address relocations in data
  1147  	check(*addr[0], 0)
  1148  	check(*addr[1], 23)
  1149  	check(*addr[2], 0)
  1150  	check(*addr[3], 0)
  1151  	check(*addr[4], 24)
  1152  	check(*addr[5], 0)
  1153  }
  1154  
  1155  func check(x, y byte) {
  1156  	if x != y {
  1157  		panic("FAIL")
  1158  	}
  1159  }
  1160  `
  1161  
  1162  func TestLargeReloc(t *testing.T) {
  1163  	// Test that large relocation addend is handled correctly.
  1164  	// In particular, on darwin/arm64 when external linking,
  1165  	// Mach-O relocation has only 24-bit addend. See issue #42738.
  1166  	testenv.MustHaveGoBuild(t)
  1167  	t.Parallel()
  1168  
  1169  	tmpdir := t.TempDir()
  1170  
  1171  	src := filepath.Join(tmpdir, "x.go")
  1172  	err := os.WriteFile(src, []byte(testLargeRelocSrc), 0666)
  1173  	if err != nil {
  1174  		t.Fatalf("failed to write source file: %v", err)
  1175  	}
  1176  	cmd := testenv.Command(t, testenv.GoToolPath(t), "run", src)
  1177  	out, err := cmd.CombinedOutput()
  1178  	if err != nil {
  1179  		t.Errorf("build failed: %v. output:\n%s", err, out)
  1180  	}
  1181  
  1182  	if testenv.HasCGO() { // currently all targets that support cgo can external link
  1183  		cmd = testenv.Command(t, testenv.GoToolPath(t), "run", "-ldflags=-linkmode=external", src)
  1184  		out, err = cmd.CombinedOutput()
  1185  		if err != nil {
  1186  			t.Fatalf("build failed: %v. output:\n%s", err, out)
  1187  		}
  1188  	}
  1189  }
  1190  
  1191  func TestUnlinkableObj(t *testing.T) {
  1192  	// Test that the linker emits an error with unlinkable object.
  1193  	testenv.MustHaveGoBuild(t)
  1194  	t.Parallel()
  1195  
  1196  	if true /* was buildcfg.Experiment.Unified */ {
  1197  		t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified")
  1198  	}
  1199  
  1200  	tmpdir := t.TempDir()
  1201  
  1202  	xSrc := filepath.Join(tmpdir, "x.go")
  1203  	pSrc := filepath.Join(tmpdir, "p.go")
  1204  	xObj := filepath.Join(tmpdir, "x.o")
  1205  	pObj := filepath.Join(tmpdir, "p.o")
  1206  	exe := filepath.Join(tmpdir, "x.exe")
  1207  	importcfgfile := filepath.Join(tmpdir, "importcfg")
  1208  	testenv.WriteImportcfg(t, importcfgfile, map[string]string{"p": pObj})
  1209  	err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666)
  1210  	if err != nil {
  1211  		t.Fatalf("failed to write source file: %v", err)
  1212  	}
  1213  	err = os.WriteFile(pSrc, []byte("package p\n"), 0666)
  1214  	if err != nil {
  1215  		t.Fatalf("failed to write source file: %v", err)
  1216  	}
  1217  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", pObj, pSrc) // without -p
  1218  	out, err := cmd.CombinedOutput()
  1219  	if err != nil {
  1220  		t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
  1221  	}
  1222  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "-o", xObj, xSrc)
  1223  	out, err = cmd.CombinedOutput()
  1224  	if err != nil {
  1225  		t.Fatalf("compile x.go failed: %v. output:\n%s", err, out)
  1226  	}
  1227  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-o", exe, xObj)
  1228  	out, err = cmd.CombinedOutput()
  1229  	if err == nil {
  1230  		t.Fatalf("link did not fail")
  1231  	}
  1232  	if !bytes.Contains(out, []byte("unlinkable object")) {
  1233  		t.Errorf("did not see expected error message. out:\n%s", out)
  1234  	}
  1235  
  1236  	// It is okay to omit -p for (only) main package.
  1237  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", pObj, pSrc)
  1238  	out, err = cmd.CombinedOutput()
  1239  	if err != nil {
  1240  		t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
  1241  	}
  1242  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", xObj, xSrc) // without -p
  1243  	out, err = cmd.CombinedOutput()
  1244  	if err != nil {
  1245  		t.Fatalf("compile failed: %v. output:\n%s", err, out)
  1246  	}
  1247  
  1248  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "link", "-importcfg="+importcfgfile, "-o", exe, xObj)
  1249  	out, err = cmd.CombinedOutput()
  1250  	if err != nil {
  1251  		t.Errorf("link failed: %v. output:\n%s", err, out)
  1252  	}
  1253  }
  1254  
  1255  func TestExtLinkCmdlineDeterminism(t *testing.T) {
  1256  	// Test that we pass flags in deterministic order to the external linker
  1257  	testenv.MustHaveGoBuild(t)
  1258  	testenv.MustHaveCGO(t) // this test requires -linkmode=external
  1259  	t.Parallel()
  1260  
  1261  	// test source code, with some cgo exports
  1262  	testSrc := `
  1263  package main
  1264  import "C"
  1265  //export F1
  1266  func F1() {}
  1267  //export F2
  1268  func F2() {}
  1269  //export F3
  1270  func F3() {}
  1271  func main() {}
  1272  `
  1273  
  1274  	tmpdir := t.TempDir()
  1275  	src := filepath.Join(tmpdir, "x.go")
  1276  	if err := os.WriteFile(src, []byte(testSrc), 0666); err != nil {
  1277  		t.Fatal(err)
  1278  	}
  1279  	exe := filepath.Join(tmpdir, "x.exe")
  1280  
  1281  	// Use a deterministic tmp directory so the temporary file paths are
  1282  	// deterministic.
  1283  	linktmp := filepath.Join(tmpdir, "linktmp")
  1284  	if err := os.Mkdir(linktmp, 0777); err != nil {
  1285  		t.Fatal(err)
  1286  	}
  1287  
  1288  	// Link with -v -linkmode=external to see the flags we pass to the
  1289  	// external linker.
  1290  	ldflags := "-ldflags=-v -linkmode=external -tmpdir=" + linktmp
  1291  	var out0 []byte
  1292  	for i := 0; i < 5; i++ {
  1293  		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", ldflags, "-o", exe, src)
  1294  		out, err := cmd.CombinedOutput()
  1295  		if err != nil {
  1296  			t.Fatalf("build failed: %v, output:\n%s", err, out)
  1297  		}
  1298  		if err := os.Remove(exe); err != nil {
  1299  			t.Fatal(err)
  1300  		}
  1301  
  1302  		// extract the "host link" invocation
  1303  		j := bytes.Index(out, []byte("\nhost link:"))
  1304  		if j == -1 {
  1305  			t.Fatalf("host link step not found, output:\n%s", out)
  1306  		}
  1307  		out = out[j+1:]
  1308  		k := bytes.Index(out, []byte("\n"))
  1309  		if k == -1 {
  1310  			t.Fatalf("no newline after host link, output:\n%s", out)
  1311  		}
  1312  		out = out[:k]
  1313  
  1314  		// filter out output file name, which is passed by the go
  1315  		// command and is nondeterministic.
  1316  		fs := bytes.Fields(out)
  1317  		for i, f := range fs {
  1318  			if bytes.Equal(f, []byte(`"-o"`)) && i+1 < len(fs) {
  1319  				fs[i+1] = []byte("a.out")
  1320  				break
  1321  			}
  1322  		}
  1323  		out = bytes.Join(fs, []byte{' '})
  1324  
  1325  		if i == 0 {
  1326  			out0 = out
  1327  			continue
  1328  		}
  1329  		if !bytes.Equal(out0, out) {
  1330  			t.Fatalf("output differ:\n%s\n==========\n%s", out0, out)
  1331  		}
  1332  	}
  1333  }
  1334  
  1335  // TestResponseFile tests that creating a response file to pass to the
  1336  // external linker works correctly.
  1337  func TestResponseFile(t *testing.T) {
  1338  	t.Parallel()
  1339  
  1340  	testenv.MustHaveGoBuild(t)
  1341  
  1342  	// This test requires -linkmode=external. Currently all
  1343  	// systems that support cgo support -linkmode=external.
  1344  	testenv.MustHaveCGO(t)
  1345  
  1346  	tmpdir := t.TempDir()
  1347  
  1348  	src := filepath.Join(tmpdir, "x.go")
  1349  	if err := os.WriteFile(src, []byte(`package main; import "C"; func main() {}`), 0666); err != nil {
  1350  		t.Fatal(err)
  1351  	}
  1352  
  1353  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "output", "x.go")
  1354  	cmd.Dir = tmpdir
  1355  
  1356  	// Add enough arguments to push cmd/link into creating a response file.
  1357  	var sb strings.Builder
  1358  	sb.WriteString(`'-ldflags=all="-extldflags=`)
  1359  	for i := 0; i < sys.ExecArgLengthLimit/len("-g"); i++ {
  1360  		if i > 0 {
  1361  			sb.WriteString(" ")
  1362  		}
  1363  		sb.WriteString("-g")
  1364  	}
  1365  	sb.WriteString(`"'`)
  1366  	cmd = testenv.CleanCmdEnv(cmd)
  1367  	cmd.Env = append(cmd.Env, "GOFLAGS="+sb.String())
  1368  
  1369  	out, err := cmd.CombinedOutput()
  1370  	if len(out) > 0 {
  1371  		t.Logf("%s", out)
  1372  	}
  1373  	if err != nil {
  1374  		t.Error(err)
  1375  	}
  1376  }
  1377  
  1378  func TestDynimportVar(t *testing.T) {
  1379  	// Test that we can access dynamically imported variables.
  1380  	// Currently darwin only.
  1381  	if runtime.GOOS != "darwin" {
  1382  		t.Skip("skip on non-darwin platform")
  1383  	}
  1384  
  1385  	testenv.MustHaveGoBuild(t)
  1386  	testenv.MustHaveCGO(t)
  1387  
  1388  	t.Parallel()
  1389  
  1390  	tmpdir := t.TempDir()
  1391  	exe := filepath.Join(tmpdir, "a.exe")
  1392  	src := filepath.Join("testdata", "dynimportvar", "main.go")
  1393  
  1394  	for _, mode := range []string{"internal", "external"} {
  1395  		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-linkmode="+mode, "-o", exe, src)
  1396  		out, err := cmd.CombinedOutput()
  1397  		if err != nil {
  1398  			t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
  1399  		}
  1400  		cmd = testenv.Command(t, exe)
  1401  		out, err = cmd.CombinedOutput()
  1402  		if err != nil {
  1403  			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
  1404  		}
  1405  	}
  1406  }
  1407  
  1408  const helloSrc = `
  1409  package main
  1410  var X = 42
  1411  var Y int
  1412  func main() { println("hello", X, Y) }
  1413  `
  1414  
  1415  func TestFlagS(t *testing.T) {
  1416  	// Test that the -s flag strips the symbol table.
  1417  	testenv.MustHaveGoBuild(t)
  1418  
  1419  	t.Parallel()
  1420  
  1421  	tmpdir := t.TempDir()
  1422  	exe := filepath.Join(tmpdir, "a.exe")
  1423  	src := filepath.Join(tmpdir, "a.go")
  1424  	err := os.WriteFile(src, []byte(helloSrc), 0666)
  1425  	if err != nil {
  1426  		t.Fatal(err)
  1427  	}
  1428  
  1429  	modes := []string{"auto"}
  1430  	if testenv.HasCGO() {
  1431  		modes = append(modes, "external")
  1432  	}
  1433  
  1434  	// check a text symbol, a data symbol, and a BSS symbol
  1435  	syms := []string{"main.main", "main.X", "main.Y"}
  1436  
  1437  	for _, mode := range modes {
  1438  		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-s -linkmode="+mode, "-o", exe, src)
  1439  		out, err := cmd.CombinedOutput()
  1440  		if err != nil {
  1441  			t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
  1442  		}
  1443  		cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
  1444  		out, err = cmd.CombinedOutput()
  1445  		if err != nil && !errors.As(err, new(*exec.ExitError)) {
  1446  			// Error exit is fine as it may have no symbols.
  1447  			// On darwin we need to emit dynamic symbol references so it
  1448  			// actually has some symbols, and nm succeeds.
  1449  			t.Errorf("(mode=%s) go tool nm failed: %v\n%s", mode, err, out)
  1450  		}
  1451  		for _, s := range syms {
  1452  			if bytes.Contains(out, []byte(s)) {
  1453  				t.Errorf("(mode=%s): unexpected symbol %s", mode, s)
  1454  			}
  1455  		}
  1456  	}
  1457  }
  1458  
  1459  func TestRandLayout(t *testing.T) {
  1460  	// Test that the -randlayout flag randomizes function order and
  1461  	// generates a working binary.
  1462  	testenv.MustHaveGoBuild(t)
  1463  
  1464  	t.Parallel()
  1465  
  1466  	tmpdir := t.TempDir()
  1467  
  1468  	src := filepath.Join(tmpdir, "hello.go")
  1469  	err := os.WriteFile(src, []byte(trivialSrc), 0666)
  1470  	if err != nil {
  1471  		t.Fatal(err)
  1472  	}
  1473  
  1474  	var syms [2]string
  1475  	for i, seed := range []string{"123", "456"} {
  1476  		exe := filepath.Join(tmpdir, "hello"+seed+".exe")
  1477  		cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-randlayout="+seed, "-o", exe, src)
  1478  		out, err := cmd.CombinedOutput()
  1479  		if err != nil {
  1480  			t.Fatalf("seed=%v: build failed: %v\n%s", seed, err, out)
  1481  		}
  1482  		cmd = testenv.Command(t, exe)
  1483  		err = cmd.Run()
  1484  		if err != nil {
  1485  			t.Fatalf("seed=%v: executable failed to run: %v\n%s", seed, err, out)
  1486  		}
  1487  		cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
  1488  		out, err = cmd.CombinedOutput()
  1489  		if err != nil {
  1490  			t.Fatalf("seed=%v: fail to run \"go tool nm\": %v\n%s", seed, err, out)
  1491  		}
  1492  		syms[i] = string(out)
  1493  	}
  1494  	if syms[0] == syms[1] {
  1495  		t.Errorf("randlayout with different seeds produced same layout:\n%s\n===\n\n%s", syms[0], syms[1])
  1496  	}
  1497  }
  1498  
  1499  func TestCheckLinkname(t *testing.T) {
  1500  	// Test that code containing blocked linknames does not build.
  1501  	testenv.MustHaveGoBuild(t)
  1502  	t.Parallel()
  1503  
  1504  	tmpdir := t.TempDir()
  1505  
  1506  	tests := []struct {
  1507  		src string
  1508  		ok  bool
  1509  	}{
  1510  		// use (instantiation) of public API is ok
  1511  		{"ok.go", true},
  1512  		// push linkname is ok
  1513  		{"push.go", true},
  1514  		// pull linkname of blocked symbol is not ok
  1515  		{"coro.go", false},
  1516  		{"coro_var.go", false},
  1517  		// assembly reference is not ok
  1518  		{"coro_asm", false},
  1519  		// pull-only linkname is not ok
  1520  		{"coro2.go", false},
  1521  		// legacy bad linkname is ok, for now
  1522  		{"fastrand.go", true},
  1523  		{"badlinkname.go", true},
  1524  	}
  1525  	for _, test := range tests {
  1526  		test := test
  1527  		t.Run(test.src, func(t *testing.T) {
  1528  			t.Parallel()
  1529  			src := filepath.Join("testdata", "linkname", test.src)
  1530  			exe := filepath.Join(tmpdir, test.src+".exe")
  1531  			cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", exe, src)
  1532  			out, err := cmd.CombinedOutput()
  1533  			if test.ok && err != nil {
  1534  				t.Errorf("build failed unexpectedly: %v:\n%s", err, out)
  1535  			}
  1536  			if !test.ok && err == nil {
  1537  				t.Errorf("build succeeded unexpectedly: %v:\n%s", err, out)
  1538  			}
  1539  		})
  1540  	}
  1541  }
  1542  

View as plain text