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

View as plain text