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/elf"
    11  	"debug/macho"
    12  	"debug/pe"
    13  	"errors"
    14  	"internal/abi"
    15  	"internal/platform"
    16  	"internal/testenv"
    17  	"internal/xcoff"
    18  	"os"
    19  	"os/exec"
    20  	"path/filepath"
    21  	"regexp"
    22  	"runtime"
    23  	"strconv"
    24  	"strings"
    25  	"testing"
    26  	"unsafe"
    27  
    28  	imacho "cmd/internal/macho"
    29  	"cmd/internal/objfile"
    30  	"cmd/internal/sys"
    31  )
    32  
    33  // TestMain allows this test binary to run as a -toolexec wrapper for
    34  // the 'go' command. If LINK_TEST_TOOLEXEC is set, TestMain runs the
    35  // binary as if it were cmd/link, and otherwise runs the requested
    36  // tool as a subprocess.
    37  //
    38  // This allows the test to verify the behavior of the current contents of the
    39  // cmd/link package even if the installed cmd/link binary is stale.
    40  func TestMain(m *testing.M) {
    41  	// Are we running as a toolexec wrapper? If so then run either
    42  	// the correct tool or this executable itself (for the linker).
    43  	// Running as toolexec wrapper.
    44  	if os.Getenv("LINK_TEST_TOOLEXEC") != "" {
    45  		if strings.TrimSuffix(filepath.Base(os.Args[1]), ".exe") == "link" {
    46  			// Running as a -toolexec linker, and the tool is cmd/link.
    47  			// Substitute this test binary for the linker.
    48  			os.Args = os.Args[1:]
    49  			main()
    50  			os.Exit(0)
    51  		}
    52  		// Running some other tool.
    53  		cmd := exec.Command(os.Args[1], os.Args[2:]...)
    54  		cmd.Stdin = os.Stdin
    55  		cmd.Stdout = os.Stdout
    56  		cmd.Stderr = os.Stderr
    57  		if err := cmd.Run(); err != nil {
    58  			os.Exit(1)
    59  		}
    60  		os.Exit(0)
    61  	}
    62  
    63  	// Are we being asked to run as the linker (without toolexec)?
    64  	// If so then kick off main.
    65  	if os.Getenv("LINK_TEST_EXEC_LINKER") != "" {
    66  		main()
    67  		os.Exit(0)
    68  	}
    69  
    70  	if testExe, err := os.Executable(); err == nil {
    71  		// on wasm, some phones, we expect an error from os.Executable()
    72  		testLinker = testExe
    73  	}
    74  
    75  	// Not running as a -toolexec wrapper or as a linker executable.
    76  	// Just run the tests.
    77  	os.Exit(m.Run())
    78  }
    79  
    80  // testLinker is the path of the test executable being run.
    81  // This is used by [TestScript].
    82  var testLinker string
    83  
    84  // goCmd returns a [*exec.Cmd] that runs the go tool using
    85  // the current linker sources rather than the installed linker.
    86  // The first element of the args parameter should be the go subcommand
    87  // to run, such as "build" or "run". It must be a subcommand that
    88  // takes the go command's build flags.
    89  func goCmd(t *testing.T, args ...string) *exec.Cmd {
    90  	goArgs := []string{args[0], "-toolexec", testenv.Executable(t)}
    91  	args = append(goArgs, args[1:]...)
    92  	cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
    93  	cmd = testenv.CleanCmdEnv(cmd)
    94  	cmd.Env = append(cmd.Env, "LINK_TEST_TOOLEXEC=1")
    95  	return cmd
    96  }
    97  
    98  // linkCmd returns a [*exec.Cmd] that runs the linker built from
    99  // the current sources. This is like "go tool link", but runs the
   100  // current linker rather than the installed one.
   101  func linkCmd(t *testing.T, args ...string) *exec.Cmd {
   102  	// Set up the arguments that TestMain looks for.
   103  	args = append([]string{"link"}, args...)
   104  	cmd := testenv.Command(t, testenv.Executable(t), args...)
   105  	cmd = testenv.CleanCmdEnv(cmd)
   106  	cmd.Env = append(cmd.Env, "LINK_TEST_TOOLEXEC=1")
   107  	return cmd
   108  }
   109  
   110  var AuthorPaidByTheColumnInch struct {
   111  	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."`
   112  
   113  	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."`
   114  
   115  	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."`
   116  
   117  	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."`
   118  }
   119  
   120  func TestLargeSymName(t *testing.T) {
   121  	// The compiler generates a symbol name using the string form of the
   122  	// type. This tests that the linker can read symbol names larger than
   123  	// the bufio buffer. Issue #15104.
   124  	_ = AuthorPaidByTheColumnInch
   125  }
   126  
   127  func TestIssue21703(t *testing.T) {
   128  	t.Parallel()
   129  
   130  	testenv.MustHaveGoBuild(t)
   131  	// N.B. the build below explictly doesn't pass through
   132  	// -asan/-msan/-race, so we don't care about those.
   133  	testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
   134  
   135  	const source = `
   136  package main
   137  const X = "\n!\n"
   138  func main() {}
   139  `
   140  
   141  	tmpdir := t.TempDir()
   142  	main := filepath.Join(tmpdir, "main.go")
   143  
   144  	err := os.WriteFile(main, []byte(source), 0666)
   145  	if err != nil {
   146  		t.Fatalf("failed to write main.go: %v\n", err)
   147  	}
   148  
   149  	importcfgfile := filepath.Join(tmpdir, "importcfg")
   150  	testenv.WriteImportcfg(t, importcfgfile, nil, main)
   151  
   152  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
   153  	cmd.Dir = tmpdir
   154  	out, err := cmd.CombinedOutput()
   155  	if err != nil {
   156  		t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out)
   157  	}
   158  
   159  	cmd = linkCmd(t, "-importcfg="+importcfgfile, "main.o")
   160  	cmd.Dir = tmpdir
   161  	out, err = cmd.CombinedOutput()
   162  	if err != nil {
   163  		if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
   164  			testenv.SkipFlaky(t, 58806)
   165  		}
   166  		t.Fatalf("failed to link main.o: %v, output: %s\n", err, out)
   167  	}
   168  }
   169  
   170  // TestIssue28429 ensures that the linker does not attempt to link
   171  // sections not named *.o. Such sections may be used by a build system
   172  // to, for example, save facts produced by a modular static analysis
   173  // such as golang.org/x/tools/go/analysis.
   174  func TestIssue28429(t *testing.T) {
   175  	t.Parallel()
   176  
   177  	testenv.MustHaveGoBuild(t)
   178  	// N.B. go build below explictly doesn't pass through
   179  	// -asan/-msan/-race, so we don't care about those.
   180  	testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
   181  
   182  	tmpdir := t.TempDir()
   183  
   184  	write := func(name, content string) {
   185  		err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
   186  		if err != nil {
   187  			t.Fatal(err)
   188  		}
   189  	}
   190  
   191  	runGo := func(args ...string) {
   192  		cmd := testenv.Command(t, testenv.GoToolPath(t), args...)
   193  		cmd.Dir = tmpdir
   194  		out, err := cmd.CombinedOutput()
   195  		if err != nil {
   196  			t.Fatalf("'go %s' failed: %v, output: %s",
   197  				strings.Join(args, " "), err, out)
   198  		}
   199  	}
   200  
   201  	// Compile a main package.
   202  	write("main.go", "package main; func main() {}")
   203  	importcfgfile := filepath.Join(tmpdir, "importcfg")
   204  	testenv.WriteImportcfg(t, importcfgfile, nil, filepath.Join(tmpdir, "main.go"))
   205  	runGo("tool", "compile", "-importcfg="+importcfgfile, "-p=main", "main.go")
   206  	runGo("tool", "pack", "c", "main.a", "main.o")
   207  
   208  	// Add an extra section with a short, non-.o name.
   209  	// This simulates an alternative build system.
   210  	write(".facts", "this is not an object file")
   211  	runGo("tool", "pack", "r", "main.a", ".facts")
   212  
   213  	// Verify that the linker does not attempt
   214  	// to compile the extra section.
   215  	cmd := linkCmd(t, "-importcfg="+importcfgfile, "main.a")
   216  	cmd.Dir = tmpdir
   217  	out, err := cmd.CombinedOutput()
   218  	if err != nil {
   219  		if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
   220  			testenv.SkipFlaky(t, 58806)
   221  		}
   222  		t.Fatalf("linker failed: %v, output %s", err, out)
   223  	}
   224  }
   225  
   226  func TestUnresolved(t *testing.T) {
   227  	testenv.MustHaveGoBuild(t)
   228  
   229  	t.Parallel()
   230  
   231  	tmpdir := t.TempDir()
   232  
   233  	write := func(name, content string) {
   234  		err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
   235  		if err != nil {
   236  			t.Fatal(err)
   237  		}
   238  	}
   239  
   240  	// Test various undefined references. Because of issue #29852,
   241  	// this used to give confusing error messages because the
   242  	// linker would find an undefined reference to "zero" created
   243  	// by the runtime package.
   244  
   245  	write("go.mod", "module testunresolved\n")
   246  	write("main.go", `package main
   247  
   248  func main() {
   249          x()
   250  }
   251  
   252  func x()
   253  `)
   254  	write("main.s", `
   255  TEXT ·x(SB),0,$0
   256          MOVD zero<>(SB), AX
   257          MOVD zero(SB), AX
   258          MOVD ·zero(SB), AX
   259          RET
   260  `)
   261  	cmd := goCmd(t, "build")
   262  	cmd.Dir = tmpdir
   263  	cmd.Env = append(cmd.Env,
   264  		"GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath"))
   265  	out, err := cmd.CombinedOutput()
   266  	if err == nil {
   267  		t.Fatalf("expected build to fail, but it succeeded")
   268  	}
   269  	out = regexp.MustCompile("(?m)^#.*\n").ReplaceAll(out, nil)
   270  	got := string(out)
   271  	want := `main.x: relocation target zero not defined
   272  main.x: relocation target zero not defined
   273  main.x: relocation target main.zero not defined
   274  `
   275  	if want != got {
   276  		t.Fatalf("want:\n%sgot:\n%s", want, got)
   277  	}
   278  }
   279  
   280  func TestIssue33979(t *testing.T) {
   281  	testenv.MustHaveGoBuild(t)
   282  	testenv.MustHaveCGO(t)
   283  	// N.B. go build below explictly doesn't pass through
   284  	// -asan/-msan/-race, so we don't care about those.
   285  	testenv.MustInternalLink(t, testenv.SpecialBuildTypes{Cgo: true})
   286  
   287  	t.Parallel()
   288  
   289  	tmpdir := t.TempDir()
   290  
   291  	write := func(name, content string) {
   292  		err := os.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
   293  		if err != nil {
   294  			t.Fatal(err)
   295  		}
   296  	}
   297  
   298  	run := func(name string, args ...string) string {
   299  		cmd := testenv.Command(t, name, args...)
   300  		cmd.Dir = tmpdir
   301  		out, err := cmd.CombinedOutput()
   302  		if err != nil {
   303  			t.Fatalf("'go %s' failed: %v, output: %s", strings.Join(args, " "), err, out)
   304  		}
   305  		return string(out)
   306  	}
   307  	runGo := func(args ...string) string {
   308  		return run(testenv.GoToolPath(t), args...)
   309  	}
   310  
   311  	// Test object with undefined reference that was not generated
   312  	// by Go, resulting in an SXREF symbol being loaded during linking.
   313  	// Because of issue #33979, the SXREF symbol would be found during
   314  	// error reporting, resulting in confusing error messages.
   315  
   316  	write("main.go", `package main
   317  func main() {
   318          x()
   319  }
   320  func x()
   321  `)
   322  	// The following assembly must work on all architectures.
   323  	write("x.s", `
   324  TEXT ·x(SB),0,$0
   325          CALL foo(SB)
   326          RET
   327  `)
   328  	write("x.c", `
   329  void undefined();
   330  
   331  void foo() {
   332          undefined();
   333  }
   334  `)
   335  
   336  	cc := strings.TrimSpace(runGo("env", "CC"))
   337  	cflags := strings.Fields(runGo("env", "GOGCCFLAGS"))
   338  
   339  	importcfgfile := filepath.Join(tmpdir, "importcfg")
   340  	testenv.WriteImportcfg(t, importcfgfile, nil, "runtime")
   341  
   342  	// Compile, assemble and pack the Go and C code.
   343  	runGo("tool", "asm", "-p=main", "-gensymabis", "-o", "symabis", "x.s")
   344  	runGo("tool", "compile", "-importcfg="+importcfgfile, "-symabis", "symabis", "-p=main", "-o", "x1.o", "main.go")
   345  	runGo("tool", "asm", "-p=main", "-o", "x2.o", "x.s")
   346  	run(cc, append(cflags, "-c", "-o", "x3.o", "x.c")...)
   347  	runGo("tool", "pack", "c", "x.a", "x1.o", "x2.o", "x3.o")
   348  
   349  	// Now attempt to link using the internal linker.
   350  	cmd := linkCmd(t, "-importcfg="+importcfgfile, "-linkmode=internal", "x.a")
   351  	cmd.Dir = tmpdir
   352  	out, err := cmd.CombinedOutput()
   353  	if err == nil {
   354  		t.Fatalf("expected link to fail, but it succeeded")
   355  	}
   356  	re := regexp.MustCompile(`(?m)^main\(.*text\): relocation target undefined not defined$`)
   357  	if !re.Match(out) {
   358  		t.Fatalf("got:\n%q\nwant:\n%s", out, re)
   359  	}
   360  }
   361  
   362  func TestBuildForTvOS(t *testing.T) {
   363  	testenv.MustHaveCGO(t)
   364  	testenv.MustHaveGoBuild(t)
   365  
   366  	// Only run this on darwin, where we can cross build for tvOS.
   367  	if runtime.GOOS != "darwin" {
   368  		t.Skip("skipping on non-darwin platform")
   369  	}
   370  	if testing.Short() && testenv.Builder() == "" {
   371  		t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty")
   372  	}
   373  	if err := testenv.Command(t, "xcrun", "--help").Run(); err != nil {
   374  		t.Skipf("error running xcrun, required for iOS cross build: %v", err)
   375  	}
   376  
   377  	t.Parallel()
   378  
   379  	sdkPath, err := testenv.Command(t, "xcrun", "--sdk", "appletvos", "--show-sdk-path").Output()
   380  	if err != nil {
   381  		t.Skip("failed to locate appletvos SDK, skipping")
   382  	}
   383  	CC := []string{
   384  		"clang",
   385  		"-arch",
   386  		"arm64",
   387  		"-isysroot", strings.TrimSpace(string(sdkPath)),
   388  		"-mtvos-version-min=12.0",
   389  		"-fembed-bitcode",
   390  	}
   391  	CGO_LDFLAGS := []string{"-framework", "CoreFoundation"}
   392  	lib := filepath.Join("testdata", "testBuildFortvOS", "lib.go")
   393  	tmpDir := t.TempDir()
   394  
   395  	ar := filepath.Join(tmpDir, "lib.a")
   396  	cmd := goCmd(t, "build", "-buildmode=c-archive", "-o", ar, lib)
   397  	env := []string{
   398  		"CGO_ENABLED=1",
   399  		"GOOS=ios",
   400  		"GOARCH=arm64",
   401  		"CC=" + strings.Join(CC, " "),
   402  		"CGO_CFLAGS=", // ensure CGO_CFLAGS does not contain any flags. Issue #35459
   403  		"CGO_LDFLAGS=" + strings.Join(CGO_LDFLAGS, " "),
   404  	}
   405  	cmd.Env = append(cmd.Env, env...)
   406  	t.Logf("%q %v", env, cmd)
   407  	if out, err := cmd.CombinedOutput(); err != nil {
   408  		t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
   409  	}
   410  
   411  	link := testenv.Command(t, CC[0], CC[1:]...)
   412  	link.Args = append(link.Args, CGO_LDFLAGS...)
   413  	link.Args = append(link.Args, "-o", filepath.Join(tmpDir, "a.out")) // Avoid writing to package directory.
   414  	link.Args = append(link.Args, ar, filepath.Join("testdata", "testBuildFortvOS", "main.m"))
   415  	t.Log(link)
   416  	if out, err := link.CombinedOutput(); err != nil {
   417  		t.Fatalf("%v: %v:\n%s", link.Args, err, out)
   418  	}
   419  }
   420  
   421  var testXFlagSrc = `
   422  package main
   423  var X = "hello"
   424  var Z = [99999]int{99998:12345} // make it large enough to be mmaped
   425  func main() { println(X) }
   426  `
   427  
   428  func TestXFlag(t *testing.T) {
   429  	testenv.MustHaveGoBuild(t)
   430  
   431  	t.Parallel()
   432  
   433  	tmpdir := t.TempDir()
   434  
   435  	src := filepath.Join(tmpdir, "main.go")
   436  	err := os.WriteFile(src, []byte(testXFlagSrc), 0666)
   437  	if err != nil {
   438  		t.Fatal(err)
   439  	}
   440  
   441  	cmd := goCmd(t, "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src)
   442  	if out, err := cmd.CombinedOutput(); err != nil {
   443  		t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
   444  	}
   445  }
   446  
   447  var trivialSrc = `
   448  package main
   449  func main() { }
   450  `
   451  
   452  func TestMachOBuildVersion(t *testing.T) {
   453  	testenv.MustHaveGoBuild(t)
   454  
   455  	t.Parallel()
   456  
   457  	tmpdir := t.TempDir()
   458  
   459  	src := filepath.Join(tmpdir, "main.go")
   460  	err := os.WriteFile(src, []byte(trivialSrc), 0666)
   461  	if err != nil {
   462  		t.Fatal(err)
   463  	}
   464  
   465  	exe := filepath.Join(tmpdir, "main")
   466  	cmd := goCmd(t, "build", "-ldflags=-linkmode=internal", "-o", exe, src)
   467  	cmd.Env = append(cmd.Env,
   468  		"CGO_ENABLED=0",
   469  		"GOOS=darwin",
   470  		"GOARCH=amd64",
   471  	)
   472  	if out, err := cmd.CombinedOutput(); err != nil {
   473  		t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
   474  	}
   475  	exef, err := os.Open(exe)
   476  	if err != nil {
   477  		t.Fatal(err)
   478  	}
   479  	defer exef.Close()
   480  	exem, err := macho.NewFile(exef)
   481  	if err != nil {
   482  		t.Fatal(err)
   483  	}
   484  	found := false
   485  	checkMin := func(ver uint32) {
   486  		major, minor, patch := (ver>>16)&0xff, (ver>>8)&0xff, (ver>>0)&0xff
   487  		if major < 12 {
   488  			t.Errorf("LC_BUILD_VERSION version %d.%d.%d < 12.0.0", major, minor, patch)
   489  		}
   490  	}
   491  	for _, cmd := range exem.Loads {
   492  		raw := cmd.Raw()
   493  		type_ := exem.ByteOrder.Uint32(raw)
   494  		if type_ != imacho.LC_BUILD_VERSION {
   495  			continue
   496  		}
   497  		osVer := exem.ByteOrder.Uint32(raw[12:])
   498  		checkMin(osVer)
   499  		sdkVer := exem.ByteOrder.Uint32(raw[16:])
   500  		checkMin(sdkVer)
   501  		found = true
   502  		break
   503  	}
   504  	if !found {
   505  		t.Errorf("no LC_BUILD_VERSION load command found")
   506  	}
   507  }
   508  
   509  func TestMachOUUID(t *testing.T) {
   510  	testenv.MustHaveGoBuild(t)
   511  	if runtime.GOOS != "darwin" {
   512  		t.Skip("this is only for darwin")
   513  	}
   514  
   515  	t.Parallel()
   516  
   517  	tmpdir := t.TempDir()
   518  
   519  	src := filepath.Join(tmpdir, "main.go")
   520  	err := os.WriteFile(src, []byte(trivialSrc), 0666)
   521  	if err != nil {
   522  		t.Fatal(err)
   523  	}
   524  
   525  	extractUUID := func(exe string) string {
   526  		exem, err := macho.Open(exe)
   527  		if err != nil {
   528  			t.Fatal(err)
   529  		}
   530  		defer exem.Close()
   531  		for _, cmd := range exem.Loads {
   532  			raw := cmd.Raw()
   533  			type_ := exem.ByteOrder.Uint32(raw)
   534  			if type_ != imacho.LC_UUID {
   535  				continue
   536  			}
   537  			return string(raw[8:24])
   538  		}
   539  		return ""
   540  	}
   541  
   542  	tests := []struct{ name, ldflags, expect string }{
   543  		{"default", "", "gobuildid"},
   544  		{"gobuildid", "-B=gobuildid", "gobuildid"},
   545  		{"specific", "-B=0x0123456789ABCDEF0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
   546  		{"none", "-B=none", ""},
   547  	}
   548  	if testenv.HasCGO() {
   549  		for _, test := range tests {
   550  			t1 := test
   551  			t1.name += "_external"
   552  			t1.ldflags += " -linkmode=external"
   553  			tests = append(tests, t1)
   554  		}
   555  	}
   556  	for _, test := range tests {
   557  		t.Run(test.name, func(t *testing.T) {
   558  			exe := filepath.Join(tmpdir, test.name)
   559  			cmd := goCmd(t, "build", "-ldflags="+test.ldflags, "-o", exe, src)
   560  			if out, err := cmd.CombinedOutput(); err != nil {
   561  				t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
   562  			}
   563  			uuid := extractUUID(exe)
   564  			if test.expect == "gobuildid" {
   565  				// Go buildid is not known in source code. Check UUID is present,
   566  				// and satisfies UUIDv3.
   567  				if uuid == "" {
   568  					t.Fatal("expect nonempty UUID, got empty")
   569  				}
   570  				// The version number is the high 4 bits of byte 6.
   571  				if uuid[6]>>4 != 3 {
   572  					t.Errorf("expect v3 UUID, got %X (version %d)", uuid, uuid[6]>>4)
   573  				}
   574  			} else if uuid != test.expect {
   575  				t.Errorf("UUID mismatch: got %X, want %X", uuid, test.expect)
   576  			}
   577  		})
   578  	}
   579  }
   580  
   581  const Issue34788src = `
   582  
   583  package blah
   584  
   585  func Blah(i int) int {
   586  	a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
   587  	return a[i&7]
   588  }
   589  `
   590  
   591  func TestIssue34788Android386TLSSequence(t *testing.T) {
   592  	testenv.MustHaveGoBuild(t)
   593  
   594  	// This is a cross-compilation test, so it doesn't make
   595  	// sense to run it on every GOOS/GOARCH combination. Limit
   596  	// the test to amd64 + darwin/linux.
   597  	if runtime.GOARCH != "amd64" ||
   598  		(runtime.GOOS != "darwin" && runtime.GOOS != "linux") {
   599  		t.Skip("skipping on non-{linux,darwin}/amd64 platform")
   600  	}
   601  
   602  	t.Parallel()
   603  
   604  	tmpdir := t.TempDir()
   605  
   606  	src := filepath.Join(tmpdir, "blah.go")
   607  	err := os.WriteFile(src, []byte(Issue34788src), 0666)
   608  	if err != nil {
   609  		t.Fatal(err)
   610  	}
   611  
   612  	obj := filepath.Join(tmpdir, "blah.o")
   613  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-p=blah", "-o", obj, src)
   614  	cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")
   615  	if out, err := cmd.CombinedOutput(); err != nil {
   616  		t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)
   617  	}
   618  
   619  	// Run objdump on the resulting object.
   620  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "objdump", obj)
   621  	out, oerr := cmd.CombinedOutput()
   622  	if oerr != nil {
   623  		t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)
   624  	}
   625  
   626  	// Sift through the output; we should not be seeing any R_TLS_LE relocs.
   627  	scanner := bufio.NewScanner(bytes.NewReader(out))
   628  	for scanner.Scan() {
   629  		line := scanner.Text()
   630  		if strings.Contains(line, "R_TLS_LE") {
   631  			t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)
   632  		}
   633  	}
   634  }
   635  
   636  const testStrictDupGoSrc = `
   637  package main
   638  func f()
   639  func main() { f() }
   640  `
   641  
   642  const testStrictDupAsmSrc1 = `
   643  #include "textflag.h"
   644  TEXT	·f(SB), NOSPLIT|DUPOK, $0-0
   645  	RET
   646  `
   647  
   648  const testStrictDupAsmSrc2 = `
   649  #include "textflag.h"
   650  TEXT	·f(SB), NOSPLIT|DUPOK, $0-0
   651  	JMP	0(PC)
   652  `
   653  
   654  const testStrictDupAsmSrc3 = `
   655  #include "textflag.h"
   656  GLOBL ·rcon(SB), RODATA|DUPOK, $64
   657  `
   658  
   659  const testStrictDupAsmSrc4 = `
   660  #include "textflag.h"
   661  GLOBL ·rcon(SB), RODATA|DUPOK, $32
   662  `
   663  
   664  func TestStrictDup(t *testing.T) {
   665  	// Check that -strictdups flag works.
   666  	testenv.MustHaveGoBuild(t)
   667  
   668  	asmfiles := []struct {
   669  		fname   string
   670  		payload string
   671  	}{
   672  		{"a", testStrictDupAsmSrc1},
   673  		{"b", testStrictDupAsmSrc2},
   674  		{"c", testStrictDupAsmSrc3},
   675  		{"d", testStrictDupAsmSrc4},
   676  	}
   677  
   678  	t.Parallel()
   679  
   680  	tmpdir := t.TempDir()
   681  
   682  	src := filepath.Join(tmpdir, "x.go")
   683  	err := os.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
   684  	if err != nil {
   685  		t.Fatal(err)
   686  	}
   687  	for _, af := range asmfiles {
   688  		src = filepath.Join(tmpdir, af.fname+".s")
   689  		err = os.WriteFile(src, []byte(af.payload), 0666)
   690  		if err != nil {
   691  			t.Fatal(err)
   692  		}
   693  	}
   694  	src = filepath.Join(tmpdir, "go.mod")
   695  	err = os.WriteFile(src, []byte("module teststrictdup\n"), 0666)
   696  	if err != nil {
   697  		t.Fatal(err)
   698  	}
   699  
   700  	cmd := goCmd(t, "build", "-ldflags=-strictdups=1")
   701  	cmd.Dir = tmpdir
   702  	out, err := cmd.CombinedOutput()
   703  	if err != nil {
   704  		t.Errorf("linking with -strictdups=1 failed: %v\n%s", err, string(out))
   705  	}
   706  	if !bytes.Contains(out, []byte("mismatched payload")) {
   707  		t.Errorf("unexpected output:\n%s", out)
   708  	}
   709  
   710  	cmd = goCmd(t, "build", "-ldflags=-strictdups=2")
   711  	cmd.Dir = tmpdir
   712  	out, err = cmd.CombinedOutput()
   713  	if err == nil {
   714  		t.Errorf("linking with -strictdups=2 did not fail")
   715  	}
   716  	// NB: on amd64 we get the 'new length' error, on arm64 the 'different
   717  	// contents' error.
   718  	if !(bytes.Contains(out, []byte("mismatched payload: new length")) ||
   719  		bytes.Contains(out, []byte("mismatched payload: same length but different contents"))) ||
   720  		!bytes.Contains(out, []byte("mismatched payload: different sizes")) {
   721  		t.Errorf("unexpected output:\n%s", out)
   722  	}
   723  }
   724  
   725  const testFuncAlignSrc = `
   726  package main
   727  import (
   728  	"fmt"
   729  )
   730  func alignPc()
   731  var alignPcFnAddr uintptr
   732  
   733  func main() {
   734  	if alignPcFnAddr % 512 != 0 {
   735  		fmt.Printf("expected 512 bytes alignment, got %v\n", alignPcFnAddr)
   736  	} else {
   737  		fmt.Printf("PASS")
   738  	}
   739  }
   740  `
   741  
   742  var testFuncAlignAsmSources = map[string]string{
   743  	"arm64": `
   744  #include "textflag.h"
   745  
   746  TEXT	·alignPc(SB),NOSPLIT, $0-0
   747  	MOVD	$2, R0
   748  	PCALIGN	$512
   749  	MOVD	$3, R1
   750  	RET
   751  
   752  GLOBL	·alignPcFnAddr(SB),RODATA,$8
   753  DATA	·alignPcFnAddr(SB)/8,$·alignPc(SB)
   754  `,
   755  	"loong64": `
   756  #include "textflag.h"
   757  
   758  TEXT	·alignPc(SB),NOSPLIT, $0-0
   759  	MOVV	$2, R4
   760  	PCALIGN	$512
   761  	MOVV	$3, R5
   762  	RET
   763  
   764  GLOBL	·alignPcFnAddr(SB),RODATA,$8
   765  DATA	·alignPcFnAddr(SB)/8,$·alignPc(SB)
   766  `,
   767  }
   768  
   769  // TestFuncAlign verifies that the address of a function can be aligned
   770  // with a specific value on arm64 and loong64.
   771  func TestFuncAlign(t *testing.T) {
   772  	testFuncAlignAsmSrc := testFuncAlignAsmSources[runtime.GOARCH]
   773  	if len(testFuncAlignAsmSrc) == 0 || runtime.GOOS != "linux" {
   774  		t.Skip("skipping on non-linux/{arm64,loong64} platform")
   775  	}
   776  	testenv.MustHaveGoBuild(t)
   777  
   778  	t.Parallel()
   779  
   780  	tmpdir := t.TempDir()
   781  
   782  	src := filepath.Join(tmpdir, "go.mod")
   783  	err := os.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666)
   784  	if err != nil {
   785  		t.Fatal(err)
   786  	}
   787  	src = filepath.Join(tmpdir, "falign.go")
   788  	err = os.WriteFile(src, []byte(testFuncAlignSrc), 0666)
   789  	if err != nil {
   790  		t.Fatal(err)
   791  	}
   792  	src = filepath.Join(tmpdir, "falign.s")
   793  	err = os.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)
   794  	if err != nil {
   795  		t.Fatal(err)
   796  	}
   797  
   798  	cmd := goCmd(t, "build", "-o", "falign")
   799  	cmd.Dir = tmpdir
   800  	out, err := cmd.CombinedOutput()
   801  	if err != nil {
   802  		t.Errorf("build failed: %v", err)
   803  	}
   804  	cmd = testenv.Command(t, tmpdir+"/falign")
   805  	out, err = cmd.CombinedOutput()
   806  	if err != nil {
   807  		t.Errorf("failed to run with err %v, output: %s", err, out)
   808  	}
   809  	if string(out) != "PASS" {
   810  		t.Errorf("unexpected output: %s\n", out)
   811  	}
   812  }
   813  
   814  const testFuncAlignOptionSrc = `
   815  package main
   816  //go:noinline
   817  func foo() {
   818  }
   819  //go:noinline
   820  func bar() {
   821  }
   822  //go:noinline
   823  func baz() {
   824  }
   825  func main() {
   826  	foo()
   827  	bar()
   828  	baz()
   829  }
   830  `
   831  
   832  // TestFuncAlignOption verifies that the -funcalign option changes the function alignment
   833  func TestFuncAlignOption(t *testing.T) {
   834  	testenv.MustHaveGoBuild(t)
   835  
   836  	t.Parallel()
   837  
   838  	tmpdir := t.TempDir()
   839  
   840  	src := filepath.Join(tmpdir, "falign.go")
   841  	err := os.WriteFile(src, []byte(testFuncAlignOptionSrc), 0666)
   842  	if err != nil {
   843  		t.Fatal(err)
   844  	}
   845  
   846  	alignTest := func(align uint64) {
   847  		exeName := "falign.exe"
   848  		cmd := goCmd(t, "build", "-ldflags=-funcalign="+strconv.FormatUint(align, 10), "-o", exeName, "falign.go")
   849  		cmd.Dir = tmpdir
   850  		out, err := cmd.CombinedOutput()
   851  		if err != nil {
   852  			t.Errorf("build failed: %v \n%s", err, out)
   853  		}
   854  		exe := filepath.Join(tmpdir, exeName)
   855  		cmd = testenv.Command(t, exe)
   856  		out, err = cmd.CombinedOutput()
   857  		if err != nil {
   858  			t.Errorf("failed to run with err %v, output: %s", err, out)
   859  		}
   860  
   861  		// Check function alignment
   862  		f, err := objfile.Open(exe)
   863  		if err != nil {
   864  			t.Fatalf("failed to open file:%v\n", err)
   865  		}
   866  		defer f.Close()
   867  
   868  		fname := map[string]bool{"_main.foo": false,
   869  			"_main.bar": false,
   870  			"_main.baz": false}
   871  		syms, err := f.Symbols()
   872  		if err != nil {
   873  			t.Errorf("failed to get symbols with err %v", err)
   874  		}
   875  		for _, s := range syms {
   876  			fn := s.Name
   877  			if _, ok := fname[fn]; !ok {
   878  				fn = "_" + s.Name
   879  				if _, ok := fname[fn]; !ok {
   880  					continue
   881  				}
   882  			}
   883  			if s.Addr%align != 0 {
   884  				t.Fatalf("unaligned function: %s %x. Expected alignment: %d\n", fn, s.Addr, align)
   885  			}
   886  			fname[fn] = true
   887  		}
   888  		for k, v := range fname {
   889  			if !v {
   890  				t.Fatalf("function %s not found\n", k)
   891  			}
   892  		}
   893  	}
   894  	alignTest(16)
   895  	alignTest(32)
   896  }
   897  
   898  const testTrampSrc = `
   899  package main
   900  import "fmt"
   901  func main() {
   902  	fmt.Println("hello")
   903  
   904  	defer func(){
   905  		if e := recover(); e == nil {
   906  			panic("did not panic")
   907  		}
   908  	}()
   909  	f1()
   910  }
   911  
   912  // Test deferreturn trampolines. See issue #39049.
   913  func f1() { defer f2() }
   914  func f2() { panic("XXX") }
   915  `
   916  
   917  func TestTrampoline(t *testing.T) {
   918  	// Test that trampoline insertion works as expected.
   919  	// For stress test, we set -debugtramp=2 flag, which sets a very low
   920  	// threshold for trampoline generation, and essentially all cross-package
   921  	// calls will use trampolines.
   922  	buildmodes := []string{"default"}
   923  	switch runtime.GOARCH {
   924  	case "arm", "arm64", "ppc64", "loong64":
   925  	case "ppc64le":
   926  		// Trampolines are generated differently when internal linking PIE, test them too.
   927  		buildmodes = append(buildmodes, "pie")
   928  	default:
   929  		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
   930  	}
   931  
   932  	testenv.MustHaveGoBuild(t)
   933  
   934  	t.Parallel()
   935  
   936  	tmpdir := t.TempDir()
   937  
   938  	src := filepath.Join(tmpdir, "hello.go")
   939  	err := os.WriteFile(src, []byte(testTrampSrc), 0666)
   940  	if err != nil {
   941  		t.Fatal(err)
   942  	}
   943  	exe := filepath.Join(tmpdir, "hello.exe")
   944  
   945  	for _, mode := range buildmodes {
   946  		cmd := goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
   947  		out, err := cmd.CombinedOutput()
   948  		if err != nil {
   949  			t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
   950  		}
   951  		cmd = testenv.Command(t, exe)
   952  		out, err = cmd.CombinedOutput()
   953  		if err != nil {
   954  			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
   955  		}
   956  		if string(out) != "hello\n" {
   957  			t.Errorf("unexpected output (%s):\n%s", mode, out)
   958  		}
   959  
   960  		out, err = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput()
   961  		if err != nil {
   962  			t.Errorf("nm failure: %s\n%s\n", err, string(out))
   963  		}
   964  		if ok, _ := regexp.Match("T runtime.deferreturn(\\+0)?-tramp0", out); !ok {
   965  			t.Errorf("Trampoline T runtime.deferreturn(+0)?-tramp0 is missing")
   966  		}
   967  	}
   968  }
   969  
   970  const testTrampCgoSrc = `
   971  package main
   972  
   973  // #include <stdio.h>
   974  // void CHello() { printf("hello\n"); fflush(stdout); }
   975  import "C"
   976  
   977  func main() {
   978  	C.CHello()
   979  }
   980  `
   981  
   982  func TestTrampolineCgo(t *testing.T) {
   983  	// Test that trampoline insertion works for cgo code.
   984  	// For stress test, we set -debugtramp=2 flag, which sets a very low
   985  	// threshold for trampoline generation, and essentially all cross-package
   986  	// calls will use trampolines.
   987  	buildmodes := []string{"default"}
   988  	switch runtime.GOARCH {
   989  	case "arm", "arm64", "ppc64", "loong64":
   990  	case "ppc64le":
   991  		// Trampolines are generated differently when internal linking PIE, test them too.
   992  		buildmodes = append(buildmodes, "pie")
   993  	default:
   994  		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
   995  	}
   996  
   997  	testenv.MustHaveGoBuild(t)
   998  	testenv.MustHaveCGO(t)
   999  
  1000  	t.Parallel()
  1001  
  1002  	tmpdir := t.TempDir()
  1003  
  1004  	src := filepath.Join(tmpdir, "hello.go")
  1005  	err := os.WriteFile(src, []byte(testTrampCgoSrc), 0666)
  1006  	if err != nil {
  1007  		t.Fatal(err)
  1008  	}
  1009  	exe := filepath.Join(tmpdir, "hello.exe")
  1010  
  1011  	for _, mode := range buildmodes {
  1012  		cmd := goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
  1013  		out, err := cmd.CombinedOutput()
  1014  		if err != nil {
  1015  			t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
  1016  		}
  1017  		cmd = testenv.Command(t, exe)
  1018  		out, err = cmd.CombinedOutput()
  1019  		if err != nil {
  1020  			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
  1021  		}
  1022  		if string(out) != "hello\n" && string(out) != "hello\r\n" {
  1023  			t.Errorf("unexpected output (%s):\n%s", mode, out)
  1024  		}
  1025  
  1026  		// Test internal linking mode.
  1027  
  1028  		if !testenv.CanInternalLink(true) {
  1029  			continue
  1030  		}
  1031  		cmd = goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
  1032  		out, err = cmd.CombinedOutput()
  1033  		if err != nil {
  1034  			t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
  1035  		}
  1036  		cmd = testenv.Command(t, exe)
  1037  		out, err = cmd.CombinedOutput()
  1038  		if err != nil {
  1039  			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
  1040  		}
  1041  		if string(out) != "hello\n" && string(out) != "hello\r\n" {
  1042  			t.Errorf("unexpected output (%s):\n%s", mode, out)
  1043  		}
  1044  	}
  1045  }
  1046  
  1047  func TestIndexMismatch(t *testing.T) {
  1048  	// Test that index mismatch will cause a link-time error (not run-time error).
  1049  	// This shouldn't happen with "go build". We invoke the compiler and the linker
  1050  	// manually, and try to "trick" the linker with an inconsistent object file.
  1051  	testenv.MustHaveGoBuild(t)
  1052  	// N.B. the build below explictly doesn't pass through
  1053  	// -asan/-msan/-race, so we don't care about those.
  1054  	testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
  1055  
  1056  	t.Parallel()
  1057  
  1058  	tmpdir := t.TempDir()
  1059  
  1060  	aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")
  1061  	bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")
  1062  	mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")
  1063  	aObj := filepath.Join(tmpdir, "a.o")
  1064  	mObj := filepath.Join(tmpdir, "main.o")
  1065  	exe := filepath.Join(tmpdir, "main.exe")
  1066  
  1067  	importcfgFile := filepath.Join(tmpdir, "runtime.importcfg")
  1068  	testenv.WriteImportcfg(t, importcfgFile, nil, "runtime")
  1069  	importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg")
  1070  	testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj}, "runtime")
  1071  
  1072  	// Build a program with main package importing package a.
  1073  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, aSrc)
  1074  	t.Log(cmd)
  1075  	out, err := cmd.CombinedOutput()
  1076  	if err != nil {
  1077  		t.Fatalf("compiling a.go failed: %v\n%s", err, out)
  1078  	}
  1079  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-I", tmpdir, "-o", mObj, mSrc)
  1080  	t.Log(cmd)
  1081  	out, err = cmd.CombinedOutput()
  1082  	if err != nil {
  1083  		t.Fatalf("compiling main.go failed: %v\n%s", err, out)
  1084  	}
  1085  	cmd = linkCmd(t, "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
  1086  	t.Log(cmd)
  1087  	out, err = cmd.CombinedOutput()
  1088  	if err != nil {
  1089  		if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
  1090  			testenv.SkipFlaky(t, 58806)
  1091  		}
  1092  		t.Errorf("linking failed: %v\n%s", err, out)
  1093  	}
  1094  
  1095  	// Now, overwrite a.o with the object of b.go. This should
  1096  	// result in an index mismatch.
  1097  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, bSrc)
  1098  	t.Log(cmd)
  1099  	out, err = cmd.CombinedOutput()
  1100  	if err != nil {
  1101  		t.Fatalf("compiling a.go failed: %v\n%s", err, out)
  1102  	}
  1103  	cmd = linkCmd(t, "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
  1104  	t.Log(cmd)
  1105  	out, err = cmd.CombinedOutput()
  1106  	if err == nil {
  1107  		t.Fatalf("linking didn't fail")
  1108  	}
  1109  	if !bytes.Contains(out, []byte("fingerprint mismatch")) {
  1110  		t.Errorf("did not see expected error message. out:\n%s", out)
  1111  	}
  1112  }
  1113  
  1114  func TestPErsrcBinutils(t *testing.T) {
  1115  	// Test that PE rsrc section is handled correctly (issue 39658).
  1116  	testenv.MustHaveGoBuild(t)
  1117  
  1118  	if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" {
  1119  		// This test is limited to amd64 and 386, because binutils is limited as such
  1120  		t.Skipf("this is only for windows/amd64 and windows/386")
  1121  	}
  1122  
  1123  	t.Parallel()
  1124  
  1125  	tmpdir := t.TempDir()
  1126  
  1127  	pkgdir := filepath.Join("testdata", "pe-binutils")
  1128  	exe := filepath.Join(tmpdir, "a.exe")
  1129  	cmd := goCmd(t, "build", "-o", exe)
  1130  	cmd.Dir = pkgdir
  1131  	// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
  1132  	out, err := cmd.CombinedOutput()
  1133  	if err != nil {
  1134  		t.Fatalf("building failed: %v, output:\n%s", err, out)
  1135  	}
  1136  
  1137  	// Check that the binary contains the rsrc data
  1138  	b, err := os.ReadFile(exe)
  1139  	if err != nil {
  1140  		t.Fatalf("reading output failed: %v", err)
  1141  	}
  1142  	if !bytes.Contains(b, []byte("Hello Gophers!")) {
  1143  		t.Fatalf("binary does not contain expected content")
  1144  	}
  1145  }
  1146  
  1147  func TestPErsrcLLVM(t *testing.T) {
  1148  	// Test that PE rsrc section is handled correctly (issue 39658).
  1149  	testenv.MustHaveGoBuild(t)
  1150  
  1151  	if runtime.GOOS != "windows" {
  1152  		t.Skipf("this is a windows-only test")
  1153  	}
  1154  
  1155  	t.Parallel()
  1156  
  1157  	tmpdir := t.TempDir()
  1158  
  1159  	pkgdir := filepath.Join("testdata", "pe-llvm")
  1160  	exe := filepath.Join(tmpdir, "a.exe")
  1161  	cmd := goCmd(t, "build", "-o", exe)
  1162  	cmd.Dir = pkgdir
  1163  	// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
  1164  	out, err := cmd.CombinedOutput()
  1165  	if err != nil {
  1166  		t.Fatalf("building failed: %v, output:\n%s", err, out)
  1167  	}
  1168  
  1169  	// Check that the binary contains the rsrc data
  1170  	b, err := os.ReadFile(exe)
  1171  	if err != nil {
  1172  		t.Fatalf("reading output failed: %v", err)
  1173  	}
  1174  	if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
  1175  		t.Fatalf("binary does not contain expected content")
  1176  	}
  1177  }
  1178  
  1179  func TestContentAddressableSymbols(t *testing.T) {
  1180  	// Test that the linker handles content-addressable symbols correctly.
  1181  	testenv.MustHaveGoBuild(t)
  1182  
  1183  	t.Parallel()
  1184  
  1185  	src := filepath.Join("testdata", "testHashedSyms", "p.go")
  1186  	cmd := goCmd(t, "run", src)
  1187  	out, err := cmd.CombinedOutput()
  1188  	if err != nil {
  1189  		t.Errorf("command %s failed: %v\n%s", cmd, err, out)
  1190  	}
  1191  }
  1192  
  1193  func TestReadOnly(t *testing.T) {
  1194  	// Test that read-only data is indeed read-only.
  1195  	testenv.MustHaveGoBuild(t)
  1196  
  1197  	t.Parallel()
  1198  
  1199  	src := filepath.Join("testdata", "testRO", "x.go")
  1200  	cmd := goCmd(t, "run", src)
  1201  	out, err := cmd.CombinedOutput()
  1202  	if err == nil {
  1203  		t.Errorf("running test program did not fail. output:\n%s", out)
  1204  	}
  1205  }
  1206  
  1207  const testIssue38554Src = `
  1208  package main
  1209  
  1210  type T [10<<20]byte
  1211  
  1212  //go:noinline
  1213  func f() T {
  1214  	return T{} // compiler will make a large stmp symbol, but not used.
  1215  }
  1216  
  1217  func main() {
  1218  	x := f()
  1219  	println(x[1])
  1220  }
  1221  `
  1222  
  1223  func TestIssue38554(t *testing.T) {
  1224  	testenv.MustHaveGoBuild(t)
  1225  
  1226  	t.Parallel()
  1227  
  1228  	tmpdir := t.TempDir()
  1229  
  1230  	src := filepath.Join(tmpdir, "x.go")
  1231  	err := os.WriteFile(src, []byte(testIssue38554Src), 0666)
  1232  	if err != nil {
  1233  		t.Fatalf("failed to write source file: %v", err)
  1234  	}
  1235  	exe := filepath.Join(tmpdir, "x.exe")
  1236  	cmd := goCmd(t, "build", "-o", exe, src)
  1237  	out, err := cmd.CombinedOutput()
  1238  	if err != nil {
  1239  		t.Fatalf("build failed: %v\n%s", err, out)
  1240  	}
  1241  
  1242  	fi, err := os.Stat(exe)
  1243  	if err != nil {
  1244  		t.Fatalf("failed to stat output file: %v", err)
  1245  	}
  1246  
  1247  	// The test program is not much different from a helloworld, which is
  1248  	// typically a little over 1 MB. We allow 5 MB. If the bad stmp is live,
  1249  	// it will be over 10 MB.
  1250  	const want = 5 << 20
  1251  	if got := fi.Size(); got > want {
  1252  		t.Errorf("binary too big: got %d, want < %d", got, want)
  1253  	}
  1254  }
  1255  
  1256  const testIssue42396src = `
  1257  package main
  1258  
  1259  //go:noinline
  1260  //go:nosplit
  1261  func callee(x int) {
  1262  }
  1263  
  1264  func main() {
  1265  	callee(9)
  1266  }
  1267  `
  1268  
  1269  func TestIssue42396(t *testing.T) {
  1270  	testenv.MustHaveGoBuild(t)
  1271  
  1272  	if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
  1273  		t.Skip("no race detector support")
  1274  	}
  1275  
  1276  	t.Parallel()
  1277  
  1278  	tmpdir := t.TempDir()
  1279  
  1280  	src := filepath.Join(tmpdir, "main.go")
  1281  	err := os.WriteFile(src, []byte(testIssue42396src), 0666)
  1282  	if err != nil {
  1283  		t.Fatalf("failed to write source file: %v", err)
  1284  	}
  1285  	exe := filepath.Join(tmpdir, "main.exe")
  1286  	cmd := goCmd(t, "build", "-gcflags=-race", "-o", exe, src)
  1287  	out, err := cmd.CombinedOutput()
  1288  	if err == nil {
  1289  		t.Fatalf("build unexpectedly succeeded")
  1290  	}
  1291  
  1292  	// Check to make sure that we see a reasonable error message
  1293  	// and not a panic.
  1294  	if strings.Contains(string(out), "panic:") {
  1295  		t.Fatalf("build should not fail with panic:\n%s", out)
  1296  	}
  1297  	const want = "reference to undefined builtin"
  1298  	if !strings.Contains(string(out), want) {
  1299  		t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
  1300  	}
  1301  }
  1302  
  1303  const testLargeRelocSrc = `
  1304  package main
  1305  
  1306  var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
  1307  
  1308  var addr = [...]*byte{
  1309  	&x[1<<23-1],
  1310  	&x[1<<23],
  1311  	&x[1<<23+1],
  1312  	&x[1<<24-1],
  1313  	&x[1<<24],
  1314  	&x[1<<24+1],
  1315  }
  1316  
  1317  func main() {
  1318  	// check relocations in instructions
  1319  	check(x[1<<23-1], 0)
  1320  	check(x[1<<23], 23)
  1321  	check(x[1<<23+1], 0)
  1322  	check(x[1<<24-1], 0)
  1323  	check(x[1<<24], 24)
  1324  	check(x[1<<24+1], 0)
  1325  
  1326  	// check absolute address relocations in data
  1327  	check(*addr[0], 0)
  1328  	check(*addr[1], 23)
  1329  	check(*addr[2], 0)
  1330  	check(*addr[3], 0)
  1331  	check(*addr[4], 24)
  1332  	check(*addr[5], 0)
  1333  }
  1334  
  1335  func check(x, y byte) {
  1336  	if x != y {
  1337  		panic("FAIL")
  1338  	}
  1339  }
  1340  `
  1341  
  1342  func TestLargeReloc(t *testing.T) {
  1343  	// Test that large relocation addend is handled correctly.
  1344  	// In particular, on darwin/arm64 when external linking,
  1345  	// Mach-O relocation has only 24-bit addend. See issue #42738.
  1346  	testenv.MustHaveGoBuild(t)
  1347  	t.Parallel()
  1348  
  1349  	tmpdir := t.TempDir()
  1350  
  1351  	src := filepath.Join(tmpdir, "x.go")
  1352  	err := os.WriteFile(src, []byte(testLargeRelocSrc), 0666)
  1353  	if err != nil {
  1354  		t.Fatalf("failed to write source file: %v", err)
  1355  	}
  1356  	cmd := goCmd(t, "run", src)
  1357  	out, err := cmd.CombinedOutput()
  1358  	if err != nil {
  1359  		t.Errorf("build failed: %v. output:\n%s", err, out)
  1360  	}
  1361  
  1362  	if testenv.HasCGO() { // currently all targets that support cgo can external link
  1363  		cmd = goCmd(t, "run", "-ldflags=-linkmode=external", src)
  1364  		out, err = cmd.CombinedOutput()
  1365  		if err != nil {
  1366  			t.Fatalf("build failed: %v. output:\n%s", err, out)
  1367  		}
  1368  	}
  1369  }
  1370  
  1371  func TestUnlinkableObj(t *testing.T) {
  1372  	// Test that the linker emits an error with unlinkable object.
  1373  	testenv.MustHaveGoBuild(t)
  1374  	t.Parallel()
  1375  
  1376  	if true /* was buildcfg.Experiment.Unified */ {
  1377  		t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified")
  1378  	}
  1379  
  1380  	tmpdir := t.TempDir()
  1381  
  1382  	xSrc := filepath.Join(tmpdir, "x.go")
  1383  	pSrc := filepath.Join(tmpdir, "p.go")
  1384  	xObj := filepath.Join(tmpdir, "x.o")
  1385  	pObj := filepath.Join(tmpdir, "p.o")
  1386  	exe := filepath.Join(tmpdir, "x.exe")
  1387  	importcfgfile := filepath.Join(tmpdir, "importcfg")
  1388  	testenv.WriteImportcfg(t, importcfgfile, map[string]string{"p": pObj})
  1389  	err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666)
  1390  	if err != nil {
  1391  		t.Fatalf("failed to write source file: %v", err)
  1392  	}
  1393  	err = os.WriteFile(pSrc, []byte("package p\n"), 0666)
  1394  	if err != nil {
  1395  		t.Fatalf("failed to write source file: %v", err)
  1396  	}
  1397  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", pObj, pSrc) // without -p
  1398  	out, err := cmd.CombinedOutput()
  1399  	if err != nil {
  1400  		t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
  1401  	}
  1402  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "-o", xObj, xSrc)
  1403  	out, err = cmd.CombinedOutput()
  1404  	if err != nil {
  1405  		t.Fatalf("compile x.go failed: %v. output:\n%s", err, out)
  1406  	}
  1407  	cmd = linkCmd(t, "-importcfg="+importcfgfile, "-o", exe, xObj)
  1408  	out, err = cmd.CombinedOutput()
  1409  	if err == nil {
  1410  		t.Fatalf("link did not fail")
  1411  	}
  1412  	if !bytes.Contains(out, []byte("unlinkable object")) {
  1413  		t.Errorf("did not see expected error message. out:\n%s", out)
  1414  	}
  1415  
  1416  	// It is okay to omit -p for (only) main package.
  1417  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", pObj, pSrc)
  1418  	out, err = cmd.CombinedOutput()
  1419  	if err != nil {
  1420  		t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
  1421  	}
  1422  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", xObj, xSrc) // without -p
  1423  	out, err = cmd.CombinedOutput()
  1424  	if err != nil {
  1425  		t.Fatalf("compile failed: %v. output:\n%s", err, out)
  1426  	}
  1427  
  1428  	cmd = linkCmd(t, "-importcfg="+importcfgfile, "-o", exe, xObj)
  1429  	out, err = cmd.CombinedOutput()
  1430  	if err != nil {
  1431  		t.Errorf("link failed: %v. output:\n%s", err, out)
  1432  	}
  1433  }
  1434  
  1435  func TestExtLinkCmdlineDeterminism(t *testing.T) {
  1436  	// Test that we pass flags in deterministic order to the external linker
  1437  	testenv.MustHaveGoBuild(t)
  1438  	testenv.MustHaveCGO(t) // this test requires -linkmode=external
  1439  	t.Parallel()
  1440  
  1441  	// test source code, with some cgo exports
  1442  	testSrc := `
  1443  package main
  1444  import "C"
  1445  //export F1
  1446  func F1() {}
  1447  //export F2
  1448  func F2() {}
  1449  //export F3
  1450  func F3() {}
  1451  func main() {}
  1452  `
  1453  
  1454  	tmpdir := t.TempDir()
  1455  	src := filepath.Join(tmpdir, "x.go")
  1456  	if err := os.WriteFile(src, []byte(testSrc), 0666); err != nil {
  1457  		t.Fatal(err)
  1458  	}
  1459  	exe := filepath.Join(tmpdir, "x.exe")
  1460  
  1461  	// Use a deterministic tmp directory so the temporary file paths are
  1462  	// deterministic.
  1463  	linktmp := filepath.Join(tmpdir, "linktmp")
  1464  	if err := os.Mkdir(linktmp, 0777); err != nil {
  1465  		t.Fatal(err)
  1466  	}
  1467  
  1468  	// Link with -v -linkmode=external to see the flags we pass to the
  1469  	// external linker.
  1470  	ldflags := "-ldflags=-v -linkmode=external -tmpdir=" + linktmp
  1471  	var out0 []byte
  1472  	for i := 0; i < 5; i++ {
  1473  		cmd := goCmd(t, "build", ldflags, "-o", exe, src)
  1474  		out, err := cmd.CombinedOutput()
  1475  		if err != nil {
  1476  			t.Fatalf("build failed: %v, output:\n%s", err, out)
  1477  		}
  1478  		if err := os.Remove(exe); err != nil {
  1479  			t.Fatal(err)
  1480  		}
  1481  
  1482  		// extract the "host link" invocation
  1483  		j := bytes.Index(out, []byte("\nhost link:"))
  1484  		if j == -1 {
  1485  			t.Fatalf("host link step not found, output:\n%s", out)
  1486  		}
  1487  		out = out[j+1:]
  1488  		k := bytes.Index(out, []byte("\n"))
  1489  		if k == -1 {
  1490  			t.Fatalf("no newline after host link, output:\n%s", out)
  1491  		}
  1492  		out = out[:k]
  1493  
  1494  		// filter out output file name, which is passed by the go
  1495  		// command and is nondeterministic.
  1496  		fs := bytes.Fields(out)
  1497  		for i, f := range fs {
  1498  			if bytes.Equal(f, []byte(`"-o"`)) && i+1 < len(fs) {
  1499  				fs[i+1] = []byte("a.out")
  1500  				break
  1501  			}
  1502  		}
  1503  		out = bytes.Join(fs, []byte{' '})
  1504  
  1505  		if i == 0 {
  1506  			out0 = out
  1507  			continue
  1508  		}
  1509  		if !bytes.Equal(out0, out) {
  1510  			t.Fatalf("output differ:\n%s\n==========\n%s", out0, out)
  1511  		}
  1512  	}
  1513  }
  1514  
  1515  // TestResponseFile tests that creating a response file to pass to the
  1516  // external linker works correctly.
  1517  func TestResponseFile(t *testing.T) {
  1518  	t.Parallel()
  1519  
  1520  	testenv.MustHaveGoBuild(t)
  1521  
  1522  	// This test requires -linkmode=external. Currently all
  1523  	// systems that support cgo support -linkmode=external.
  1524  	testenv.MustHaveCGO(t)
  1525  
  1526  	tmpdir := t.TempDir()
  1527  
  1528  	src := filepath.Join(tmpdir, "x.go")
  1529  	if err := os.WriteFile(src, []byte(`package main; import "C"; func main() {}`), 0666); err != nil {
  1530  		t.Fatal(err)
  1531  	}
  1532  
  1533  	// We don't use goCmd here, as -toolexec doesn't use response files.
  1534  	// This test is more for the go command than the linker anyhow.
  1535  
  1536  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "output", "x.go")
  1537  	cmd.Dir = tmpdir
  1538  
  1539  	// Add enough arguments to push cmd/link into creating a response file.
  1540  	var sb strings.Builder
  1541  	sb.WriteString(`'-ldflags=all="-extldflags=`)
  1542  	for i := 0; i < sys.ExecArgLengthLimit/len("-g"); i++ {
  1543  		if i > 0 {
  1544  			sb.WriteString(" ")
  1545  		}
  1546  		sb.WriteString("-g")
  1547  	}
  1548  	sb.WriteString(`"'`)
  1549  	cmd = testenv.CleanCmdEnv(cmd)
  1550  	cmd.Env = append(cmd.Env, "GOFLAGS="+sb.String())
  1551  
  1552  	out, err := cmd.CombinedOutput()
  1553  	if len(out) > 0 {
  1554  		t.Logf("%s", out)
  1555  	}
  1556  	if err != nil {
  1557  		t.Error(err)
  1558  	}
  1559  }
  1560  
  1561  func TestDynimportVar(t *testing.T) {
  1562  	// Test that we can access dynamically imported variables.
  1563  	// Currently darwin only.
  1564  	if runtime.GOOS != "darwin" {
  1565  		t.Skip("skip on non-darwin platform")
  1566  	}
  1567  
  1568  	testenv.MustHaveGoBuild(t)
  1569  	testenv.MustHaveCGO(t)
  1570  
  1571  	t.Parallel()
  1572  
  1573  	tmpdir := t.TempDir()
  1574  	exe := filepath.Join(tmpdir, "a.exe")
  1575  	src := filepath.Join("testdata", "dynimportvar", "main.go")
  1576  
  1577  	for _, mode := range []string{"internal", "external"} {
  1578  		cmd := goCmd(t, "build", "-ldflags=-linkmode="+mode, "-o", exe, src)
  1579  		out, err := cmd.CombinedOutput()
  1580  		if err != nil {
  1581  			t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
  1582  		}
  1583  		cmd = testenv.Command(t, exe)
  1584  		out, err = cmd.CombinedOutput()
  1585  		if err != nil {
  1586  			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
  1587  		}
  1588  	}
  1589  }
  1590  
  1591  const helloSrc = `
  1592  package main
  1593  var X = 42
  1594  var Y int
  1595  func main() { println("hello", X, Y) }
  1596  `
  1597  
  1598  func TestFlagS(t *testing.T) {
  1599  	// Test that the -s flag strips the symbol table.
  1600  	testenv.MustHaveGoBuild(t)
  1601  
  1602  	t.Parallel()
  1603  
  1604  	tmpdir := t.TempDir()
  1605  	exe := filepath.Join(tmpdir, "a.exe")
  1606  	src := filepath.Join(tmpdir, "a.go")
  1607  	err := os.WriteFile(src, []byte(helloSrc), 0666)
  1608  	if err != nil {
  1609  		t.Fatal(err)
  1610  	}
  1611  
  1612  	modes := []string{"auto"}
  1613  	if testenv.HasCGO() {
  1614  		modes = append(modes, "external")
  1615  	}
  1616  
  1617  	// check a text symbol, a data symbol, and a BSS symbol
  1618  	syms := []string{"main.main", "main.X", "main.Y"}
  1619  
  1620  	for _, mode := range modes {
  1621  		cmd := goCmd(t, "build", "-ldflags=-s -linkmode="+mode, "-o", exe, src)
  1622  		out, err := cmd.CombinedOutput()
  1623  		if err != nil {
  1624  			t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
  1625  		}
  1626  		cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
  1627  		out, err = cmd.CombinedOutput()
  1628  		if err != nil {
  1629  			if _, ok := errors.AsType[*exec.ExitError](err); !ok {
  1630  				// Error exit is fine as it may have no symbols.
  1631  				// On darwin we need to emit dynamic symbol references so it
  1632  				// actually has some symbols, and nm succeeds.
  1633  				t.Errorf("(mode=%s) go tool nm failed: %v\n%s", mode, err, out)
  1634  			}
  1635  		}
  1636  		for _, s := range syms {
  1637  			if bytes.Contains(out, []byte(s)) {
  1638  				t.Errorf("(mode=%s): unexpected symbol %s", mode, s)
  1639  			}
  1640  		}
  1641  	}
  1642  }
  1643  
  1644  func TestRandLayout(t *testing.T) {
  1645  	// Test that the -randlayout flag randomizes function order and
  1646  	// generates a working binary.
  1647  	testenv.MustHaveGoBuild(t)
  1648  
  1649  	t.Parallel()
  1650  
  1651  	tmpdir := t.TempDir()
  1652  
  1653  	src := filepath.Join(tmpdir, "hello.go")
  1654  	err := os.WriteFile(src, []byte(trivialSrc), 0666)
  1655  	if err != nil {
  1656  		t.Fatal(err)
  1657  	}
  1658  
  1659  	var syms [2]string
  1660  	for i, seed := range []string{"123", "456"} {
  1661  		exe := filepath.Join(tmpdir, "hello"+seed+".exe")
  1662  		cmd := goCmd(t, "build", "-ldflags=-randlayout="+seed, "-o", exe, src)
  1663  		out, err := cmd.CombinedOutput()
  1664  		if err != nil {
  1665  			t.Fatalf("seed=%v: build failed: %v\n%s", seed, err, out)
  1666  		}
  1667  		cmd = testenv.Command(t, exe)
  1668  		err = cmd.Run()
  1669  		if err != nil {
  1670  			t.Fatalf("seed=%v: executable failed to run: %v\n%s", seed, err, out)
  1671  		}
  1672  		cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
  1673  		out, err = cmd.CombinedOutput()
  1674  		if err != nil {
  1675  			t.Fatalf("seed=%v: fail to run \"go tool nm\": %v\n%s", seed, err, out)
  1676  		}
  1677  		syms[i] = string(out)
  1678  	}
  1679  	if syms[0] == syms[1] {
  1680  		t.Errorf("randlayout with different seeds produced same layout:\n%s\n===\n\n%s", syms[0], syms[1])
  1681  	}
  1682  }
  1683  
  1684  func TestCheckLinkname(t *testing.T) {
  1685  	// Test that code containing blocked linknames does not build.
  1686  	testenv.MustHaveGoBuild(t)
  1687  	t.Parallel()
  1688  
  1689  	tmpdir := t.TempDir()
  1690  
  1691  	tests := []struct {
  1692  		src string
  1693  		ok  bool
  1694  	}{
  1695  		// use (instantiation) of public API is ok
  1696  		{"ok.go", true},
  1697  		// push linkname is ok
  1698  		{"push.go", true},
  1699  		// using a linknamed variable to reference an assembly
  1700  		// function in the same package is ok
  1701  		{"textvar", true},
  1702  		// pull linkname of blocked symbol is not ok
  1703  		{"coro.go", false},
  1704  		{"coro_var.go", false},
  1705  		// assembly reference is not ok
  1706  		{"coro_asm", false},
  1707  		// pull-only linkname is not ok
  1708  		{"coro2.go", false},
  1709  		// pull linkname of a builtin symbol is not ok
  1710  		{"builtin.go", false},
  1711  		{"addmoduledata.go", false},
  1712  		{"freegc.go", false},
  1713  		// legacy bad linkname is ok, for now
  1714  		{"fastrand.go", true},
  1715  		{"badlinkname.go", true},
  1716  	}
  1717  	for _, test := range tests {
  1718  		test := test
  1719  		t.Run(test.src, func(t *testing.T) {
  1720  			t.Parallel()
  1721  			src := "./testdata/linkname/" + test.src
  1722  			exe := filepath.Join(tmpdir, test.src+".exe")
  1723  			cmd := goCmd(t, "build", "-o", exe, src)
  1724  			out, err := cmd.CombinedOutput()
  1725  			if test.ok && err != nil {
  1726  				t.Errorf("build failed unexpectedly: %v:\n%s", err, out)
  1727  			}
  1728  			if !test.ok && err == nil {
  1729  				t.Errorf("build succeeded unexpectedly: %v:\n%s", err, out)
  1730  			}
  1731  		})
  1732  	}
  1733  }
  1734  
  1735  func TestLinknameBSS(t *testing.T) {
  1736  	// Test that the linker chooses the right one as the definition
  1737  	// for linknamed variables. See issue #72032.
  1738  	testenv.MustHaveGoBuild(t)
  1739  	t.Parallel()
  1740  
  1741  	tmpdir := t.TempDir()
  1742  
  1743  	src := filepath.Join("testdata", "linkname", "sched.go")
  1744  	exe := filepath.Join(tmpdir, "sched.exe")
  1745  	cmd := goCmd(t, "build", "-o", exe, src)
  1746  	out, err := cmd.CombinedOutput()
  1747  	if err != nil {
  1748  		t.Fatalf("build failed unexpectedly: %v:\n%s", err, out)
  1749  	}
  1750  
  1751  	// Check the symbol size.
  1752  	f, err := objfile.Open(exe)
  1753  	if err != nil {
  1754  		t.Fatalf("fail to open executable: %v", err)
  1755  	}
  1756  	defer f.Close()
  1757  	syms, err := f.Symbols()
  1758  	if err != nil {
  1759  		t.Fatalf("fail to get symbols: %v", err)
  1760  	}
  1761  	found := false
  1762  	for _, s := range syms {
  1763  		if s.Name == "runtime.sched" || s.Name == "_runtime.sched" {
  1764  			found = true
  1765  			if s.Size < 100 {
  1766  				// As of Go 1.25 (Mar 2025), runtime.sched has 6848 bytes on
  1767  				// darwin/arm64. It should always be larger than 100 bytes on
  1768  				// all platforms.
  1769  				t.Errorf("runtime.sched symbol size too small: want > 100, got %d", s.Size)
  1770  			}
  1771  		}
  1772  	}
  1773  	if !found {
  1774  		t.Errorf("runtime.sched symbol not found")
  1775  	}
  1776  
  1777  	// Executable should run.
  1778  	cmd = testenv.Command(t, exe)
  1779  	out, err = cmd.CombinedOutput()
  1780  	if err != nil {
  1781  		t.Errorf("executable failed to run: %v\n%s", err, out)
  1782  	}
  1783  }
  1784  
  1785  // setValueFromBytes copies from a []byte to a variable.
  1786  // This is used to get correctly aligned values in TestFuncdataPlacement.
  1787  func setValueFromBytes[T any](p *T, s []byte) {
  1788  	copy(unsafe.Slice((*byte)(unsafe.Pointer(p)), unsafe.Sizeof(*p)), s)
  1789  }
  1790  
  1791  // Test that all funcdata values are stored in the .gopclntab section.
  1792  // This is pretty ugly as there is no API for accessing this data.
  1793  // This test will have to be updated when the data formats change.
  1794  func TestFuncdataPlacement(t *testing.T) {
  1795  	testenv.MustHaveGoBuild(t)
  1796  	t.Parallel()
  1797  
  1798  	tmpdir := t.TempDir()
  1799  	src := filepath.Join(tmpdir, "x.go")
  1800  	if err := os.WriteFile(src, []byte(trivialSrc), 0o444); err != nil {
  1801  		t.Fatal(err)
  1802  	}
  1803  
  1804  	exe := filepath.Join(tmpdir, "x.exe")
  1805  	cmd := goCmd(t, "build", "-o", exe, src)
  1806  	if out, err := cmd.CombinedOutput(); err != nil {
  1807  		t.Fatalf("build failed; %v, output:\n%s", err, out)
  1808  	}
  1809  
  1810  	// We want to find the funcdata in the executable.
  1811  	// We look at the section table to find the .gopclntab section,
  1812  	// which starts with the pcHeader.
  1813  	// That will give us the table of functions,
  1814  	// which we can use to find the funcdata.
  1815  
  1816  	ef, _ := elf.Open(exe)
  1817  	mf, _ := macho.Open(exe)
  1818  	pf, _ := pe.Open(exe)
  1819  	xf, _ := xcoff.Open(exe)
  1820  	// TODO: plan9
  1821  	if ef == nil && mf == nil && pf == nil && xf == nil {
  1822  		t.Skip("unrecognized executable file format")
  1823  	}
  1824  
  1825  	const moddataSymName = "runtime.firstmoduledata"
  1826  	const gofuncSymName = "go:func.*"
  1827  	var (
  1828  		pclntab      []byte
  1829  		pclntabAddr  uint64
  1830  		pclntabEnd   uint64
  1831  		moddataAddr  uint64
  1832  		moddataBytes []byte
  1833  		gofuncAddr   uint64
  1834  		imageBase    uint64
  1835  	)
  1836  	switch {
  1837  	case ef != nil:
  1838  		defer ef.Close()
  1839  
  1840  		syms, err := ef.Symbols()
  1841  		if err != nil {
  1842  			t.Fatal(err)
  1843  		}
  1844  		for _, sym := range syms {
  1845  			switch sym.Name {
  1846  			case moddataSymName:
  1847  				moddataAddr = sym.Value
  1848  			case gofuncSymName:
  1849  				gofuncAddr = sym.Value
  1850  			}
  1851  		}
  1852  
  1853  		for _, sec := range ef.Sections {
  1854  			if sec.Name == ".gopclntab" {
  1855  				data, err := sec.Data()
  1856  				if err != nil {
  1857  					t.Fatal(err)
  1858  				}
  1859  				pclntab = data
  1860  				pclntabAddr = sec.Addr
  1861  				pclntabEnd = sec.Addr + sec.Size
  1862  			}
  1863  			if sec.Flags&elf.SHF_ALLOC != 0 && moddataAddr >= sec.Addr && moddataAddr < sec.Addr+sec.Size {
  1864  				data, err := sec.Data()
  1865  				if err != nil {
  1866  					t.Fatal(err)
  1867  				}
  1868  				moddataBytes = data[moddataAddr-sec.Addr:]
  1869  			}
  1870  		}
  1871  
  1872  	case mf != nil:
  1873  		defer mf.Close()
  1874  
  1875  		for _, sym := range mf.Symtab.Syms {
  1876  			switch sym.Name {
  1877  			case moddataSymName:
  1878  				moddataAddr = sym.Value
  1879  			case gofuncSymName:
  1880  				gofuncAddr = sym.Value
  1881  			}
  1882  		}
  1883  
  1884  		for _, sec := range mf.Sections {
  1885  			if sec.Name == "__gopclntab" {
  1886  				data, err := sec.Data()
  1887  				if err != nil {
  1888  					t.Fatal(err)
  1889  				}
  1890  				pclntab = data
  1891  				pclntabAddr = sec.Addr
  1892  				pclntabEnd = sec.Addr + sec.Size
  1893  			}
  1894  			if moddataAddr >= sec.Addr && moddataAddr < sec.Addr+sec.Size {
  1895  				data, err := sec.Data()
  1896  				if err != nil {
  1897  					t.Fatal(err)
  1898  				}
  1899  				moddataBytes = data[moddataAddr-sec.Addr:]
  1900  			}
  1901  		}
  1902  
  1903  	case pf != nil:
  1904  		defer pf.Close()
  1905  
  1906  		switch ohdr := pf.OptionalHeader.(type) {
  1907  		case *pe.OptionalHeader32:
  1908  			imageBase = uint64(ohdr.ImageBase)
  1909  		case *pe.OptionalHeader64:
  1910  			imageBase = ohdr.ImageBase
  1911  		}
  1912  
  1913  		var moddataSym, gofuncSym, pclntabSym, epclntabSym *pe.Symbol
  1914  		for _, sym := range pf.Symbols {
  1915  			switch sym.Name {
  1916  			case moddataSymName:
  1917  				moddataSym = sym
  1918  			case gofuncSymName:
  1919  				gofuncSym = sym
  1920  			case "runtime.pclntab":
  1921  				pclntabSym = sym
  1922  			case "runtime.epclntab":
  1923  				epclntabSym = sym
  1924  			}
  1925  		}
  1926  
  1927  		if moddataSym == nil {
  1928  			t.Fatalf("could not find symbol %s", moddataSymName)
  1929  		}
  1930  		if gofuncSym == nil {
  1931  			t.Fatalf("could not find symbol %s", gofuncSymName)
  1932  		}
  1933  		if pclntabSym == nil {
  1934  			t.Fatal("could not find symbol runtime.pclntab")
  1935  		}
  1936  		if epclntabSym == nil {
  1937  			t.Fatal("could not find symbol runtime.epclntab")
  1938  		}
  1939  
  1940  		sec := pf.Sections[moddataSym.SectionNumber-1]
  1941  		data, err := sec.Data()
  1942  		if err != nil {
  1943  			t.Fatal(err)
  1944  		}
  1945  		moddataBytes = data[moddataSym.Value:]
  1946  		moddataAddr = uint64(sec.VirtualAddress + moddataSym.Value)
  1947  
  1948  		sec = pf.Sections[gofuncSym.SectionNumber-1]
  1949  		gofuncAddr = uint64(sec.VirtualAddress + gofuncSym.Value)
  1950  
  1951  		if pclntabSym.SectionNumber != epclntabSym.SectionNumber {
  1952  			t.Fatalf("runtime.pclntab section %d != runtime.epclntab section %d", pclntabSym.SectionNumber, epclntabSym.SectionNumber)
  1953  		}
  1954  		sec = pf.Sections[pclntabSym.SectionNumber-1]
  1955  		data, err = sec.Data()
  1956  		if err != nil {
  1957  			t.Fatal(err)
  1958  		}
  1959  		pclntab = data[pclntabSym.Value:epclntabSym.Value]
  1960  		pclntabAddr = uint64(sec.VirtualAddress + pclntabSym.Value)
  1961  		pclntabEnd = uint64(sec.VirtualAddress + epclntabSym.Value)
  1962  
  1963  	case xf != nil:
  1964  		defer xf.Close()
  1965  
  1966  		var moddataSym, gofuncSym, pclntabSym, epclntabSym *xcoff.Symbol
  1967  		for _, sym := range xf.Symbols {
  1968  			switch sym.Name {
  1969  			case moddataSymName:
  1970  				moddataSym = sym
  1971  			case gofuncSymName:
  1972  				gofuncSym = sym
  1973  			case "runtime.pclntab":
  1974  				pclntabSym = sym
  1975  			case "runtime.epclntab":
  1976  				epclntabSym = sym
  1977  			}
  1978  		}
  1979  
  1980  		if moddataSym == nil {
  1981  			t.Fatalf("could not find symbol %s", moddataSymName)
  1982  		}
  1983  		if gofuncSym == nil {
  1984  			t.Fatalf("could not find symbol %s", gofuncSymName)
  1985  		}
  1986  		if pclntabSym == nil {
  1987  			t.Fatal("could not find symbol runtime.pclntab")
  1988  		}
  1989  		if epclntabSym == nil {
  1990  			t.Fatal("could not find symbol runtime.epclntab")
  1991  		}
  1992  
  1993  		sec := xf.Sections[moddataSym.SectionNumber-1]
  1994  		data, err := sec.Data()
  1995  		if err != nil {
  1996  			t.Fatal(err)
  1997  		}
  1998  		moddataBytes = data[moddataSym.Value:]
  1999  		moddataAddr = uint64(sec.VirtualAddress + moddataSym.Value)
  2000  
  2001  		sec = xf.Sections[gofuncSym.SectionNumber-1]
  2002  		gofuncAddr = uint64(sec.VirtualAddress + gofuncSym.Value)
  2003  
  2004  		if pclntabSym.SectionNumber != epclntabSym.SectionNumber {
  2005  			t.Fatalf("runtime.pclntab section %d != runtime.epclntab section %d", pclntabSym.SectionNumber, epclntabSym.SectionNumber)
  2006  		}
  2007  		sec = xf.Sections[pclntabSym.SectionNumber-1]
  2008  		data, err = sec.Data()
  2009  		if err != nil {
  2010  			t.Fatal(err)
  2011  		}
  2012  		pclntab = data[pclntabSym.Value:epclntabSym.Value]
  2013  		pclntabAddr = uint64(sec.VirtualAddress + pclntabSym.Value)
  2014  		pclntabEnd = uint64(sec.VirtualAddress + epclntabSym.Value)
  2015  
  2016  	default:
  2017  		panic("can't happen")
  2018  	}
  2019  
  2020  	if len(pclntab) == 0 {
  2021  		t.Fatal("could not find pclntab section")
  2022  	}
  2023  	if moddataAddr == 0 {
  2024  		t.Fatalf("could not find %s symbol", moddataSymName)
  2025  	}
  2026  	if gofuncAddr == 0 {
  2027  		t.Fatalf("could not find %s symbol", gofuncSymName)
  2028  	}
  2029  	if gofuncAddr < pclntabAddr || gofuncAddr >= pclntabEnd {
  2030  		t.Fatalf("%s out of range: value %#x not between %#x and %#x", gofuncSymName, gofuncAddr, pclntabAddr, pclntabEnd)
  2031  	}
  2032  	if len(moddataBytes) == 0 {
  2033  		t.Fatal("could not find module data")
  2034  	}
  2035  
  2036  	// What a slice looks like in the object file.
  2037  	type moddataSlice struct {
  2038  		addr uintptr
  2039  		len  int
  2040  		cap  int
  2041  	}
  2042  
  2043  	// This needs to match the struct defined in runtime/symtab.go,
  2044  	// and written out by (*Link).symtab.
  2045  	// This is not the complete moddata struct, only what we need here.
  2046  	type moddataType struct {
  2047  		pcHeader     uintptr
  2048  		funcnametab  moddataSlice
  2049  		cutab        moddataSlice
  2050  		filetab      moddataSlice
  2051  		pctab        moddataSlice
  2052  		pclntable    moddataSlice
  2053  		ftab         moddataSlice
  2054  		findfunctab  uintptr
  2055  		minpc, maxpc uintptr
  2056  
  2057  		text, etext           uintptr
  2058  		noptrdata, enoptrdata uintptr
  2059  		data, edata           uintptr
  2060  		bss, ebss             uintptr
  2061  		noptrbss, enoptrbss   uintptr
  2062  		covctrs, ecovctrs     uintptr
  2063  		end, gcdata, gcbss    uintptr
  2064  		types, etypes         uintptr
  2065  		rodata                uintptr
  2066  		gofunc                uintptr
  2067  	}
  2068  
  2069  	// The executable is on the same system as we are running,
  2070  	// so the sizes and alignments should match.
  2071  	// But moddataBytes itself may not be aligned as needed.
  2072  	// Copy to a variable to ensure alignment.
  2073  	var moddata moddataType
  2074  	setValueFromBytes(&moddata, moddataBytes)
  2075  
  2076  	ftabAddr := uint64(moddata.ftab.addr) - imageBase
  2077  	if ftabAddr < pclntabAddr || ftabAddr >= pclntabEnd {
  2078  		t.Fatalf("ftab address %#x not between %#x and %#x", ftabAddr, pclntabAddr, pclntabEnd)
  2079  	}
  2080  
  2081  	// From runtime/symtab.go and the linker function writePCToFunc.
  2082  	type functab struct {
  2083  		entryoff uint32
  2084  		funcoff  uint32
  2085  	}
  2086  	// The ftab slice in moddata has one extra entry used to record
  2087  	// the final PC.
  2088  	ftabLen := moddata.ftab.len - 1
  2089  	ftab := make([]functab, ftabLen)
  2090  	copy(ftab, unsafe.Slice((*functab)(unsafe.Pointer(&pclntab[ftabAddr-pclntabAddr])), ftabLen))
  2091  
  2092  	ftabBase := uint64(moddata.pclntable.addr) - imageBase
  2093  
  2094  	// From runtime/runtime2.go _func and the linker function writeFuncs.
  2095  	type funcEntry struct {
  2096  		entryOff uint32
  2097  		nameOff  int32
  2098  
  2099  		args        int32
  2100  		deferreturn uint32
  2101  
  2102  		pcsp      uint32
  2103  		pcfile    uint32
  2104  		pcln      uint32
  2105  		npcdata   uint32
  2106  		cuOffset  uint32
  2107  		startLine int32
  2108  		funcID    abi.FuncID
  2109  		flag      abi.FuncFlag
  2110  		_         [1]byte
  2111  		nfuncdata uint8
  2112  	}
  2113  
  2114  	for i, ftabEntry := range ftab {
  2115  		funcAddr := ftabBase + uint64(ftabEntry.funcoff)
  2116  		if funcAddr < pclntabAddr || funcAddr >= pclntabEnd {
  2117  			t.Errorf("ftab entry %d address %#x not between %#x and %#x", i, funcAddr, pclntabAddr, pclntabEnd)
  2118  			continue
  2119  		}
  2120  
  2121  		var fe funcEntry
  2122  		setValueFromBytes(&fe, pclntab[funcAddr-pclntabAddr:])
  2123  
  2124  		funcdataVals := funcAddr + uint64(unsafe.Sizeof(fe)) + uint64(fe.npcdata*4)
  2125  		for j := range fe.nfuncdata {
  2126  			var funcdataVal uint32
  2127  			setValueFromBytes(&funcdataVal, pclntab[funcdataVals+uint64(j)*4-pclntabAddr:])
  2128  			if funcdataVal == ^uint32(0) {
  2129  				continue
  2130  			}
  2131  			funcdataAddr := gofuncAddr + uint64(funcdataVal)
  2132  			if funcdataAddr < pclntabAddr || funcdataAddr >= pclntabEnd {
  2133  				t.Errorf("ftab entry %d funcdata %d address %#x not between %#x and %#x", i, j, funcdataAddr, pclntabAddr, pclntabEnd)
  2134  			}
  2135  		}
  2136  	}
  2137  
  2138  	if uint64(moddata.findfunctab)-imageBase < pclntabAddr || uint64(moddata.findfunctab)-imageBase >= pclntabEnd {
  2139  		t.Errorf("findfunctab address %#x not between %#x and %#x", moddata.findfunctab, pclntabAddr, pclntabEnd)
  2140  	}
  2141  }
  2142  
  2143  // Test that moduledata winds up in its own .go.module section.
  2144  func TestModuledataPlacement(t *testing.T) {
  2145  	testenv.MustHaveGoBuild(t)
  2146  	t.Parallel()
  2147  
  2148  	tmpdir := t.TempDir()
  2149  	src := filepath.Join(tmpdir, "x.go")
  2150  	if err := os.WriteFile(src, []byte(trivialSrc), 0o444); err != nil {
  2151  		t.Fatal(err)
  2152  	}
  2153  
  2154  	exe := filepath.Join(tmpdir, "x.exe")
  2155  	cmd := goCmd(t, "build", "-o", exe, src)
  2156  	if out, err := cmd.CombinedOutput(); err != nil {
  2157  		t.Fatalf("build failed; %v, output:\n%s", err, out)
  2158  	}
  2159  
  2160  	ef, _ := elf.Open(exe)
  2161  	mf, _ := macho.Open(exe)
  2162  	pf, _ := pe.Open(exe)
  2163  	xf, _ := xcoff.Open(exe)
  2164  	// TODO: plan9
  2165  	if ef == nil && mf == nil && pf == nil && xf == nil {
  2166  		t.Skip("unrecognized executable file format")
  2167  	}
  2168  
  2169  	const moddataSymName = "runtime.firstmoduledata"
  2170  	switch {
  2171  	case ef != nil:
  2172  		defer ef.Close()
  2173  
  2174  		syms, err := ef.Symbols()
  2175  		if err != nil {
  2176  			t.Fatal(err)
  2177  		}
  2178  		for _, sym := range syms {
  2179  			if sym.Name == moddataSymName {
  2180  				sec := ef.Sections[sym.Section]
  2181  				if sec.Name != ".go.module" {
  2182  					t.Errorf("moduledata in section %s, not .go.module", sec.Name)
  2183  				}
  2184  				if sym.Value != sec.Addr {
  2185  					t.Errorf("moduledata address %#x != section start address %#x", sym.Value, sec.Addr)
  2186  				}
  2187  				break
  2188  			}
  2189  		}
  2190  
  2191  	case mf != nil:
  2192  		defer mf.Close()
  2193  
  2194  		for _, sym := range mf.Symtab.Syms {
  2195  			if sym.Name == moddataSymName {
  2196  				if sym.Sect == 0 {
  2197  					t.Error("moduledata not in a section")
  2198  				} else {
  2199  					sec := mf.Sections[sym.Sect-1]
  2200  					if sec.Name != "__go_module" {
  2201  						t.Errorf("moduledata in section %s, not __go.module", sec.Name)
  2202  					}
  2203  					if sym.Value != sec.Addr {
  2204  						t.Errorf("moduledata address %#x != section start address %#x", sym.Value, sec.Addr)
  2205  					}
  2206  				}
  2207  				break
  2208  			}
  2209  		}
  2210  
  2211  	case pf != nil, xf != nil:
  2212  		if pf != nil {
  2213  			defer pf.Close()
  2214  		}
  2215  		if xf != nil {
  2216  			defer xf.Close()
  2217  		}
  2218  
  2219  		// On Windows and AIX all the Go specific sections
  2220  		// get stuffed into a few sections,
  2221  		// so there is nothing to test here.
  2222  	}
  2223  }
  2224  

View as plain text