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  		for _, s := range syms {
   873  			fn := s.Name
   874  			if _, ok := fname[fn]; !ok {
   875  				fn = "_" + s.Name
   876  				if _, ok := fname[fn]; !ok {
   877  					continue
   878  				}
   879  			}
   880  			if s.Addr%align != 0 {
   881  				t.Fatalf("unaligned function: %s %x. Expected alignment: %d\n", fn, s.Addr, align)
   882  			}
   883  			fname[fn] = true
   884  		}
   885  		for k, v := range fname {
   886  			if !v {
   887  				t.Fatalf("function %s not found\n", k)
   888  			}
   889  		}
   890  	}
   891  	alignTest(16)
   892  	alignTest(32)
   893  }
   894  
   895  const testTrampSrc = `
   896  package main
   897  import "fmt"
   898  func main() {
   899  	fmt.Println("hello")
   900  
   901  	defer func(){
   902  		if e := recover(); e == nil {
   903  			panic("did not panic")
   904  		}
   905  	}()
   906  	f1()
   907  }
   908  
   909  // Test deferreturn trampolines. See issue #39049.
   910  func f1() { defer f2() }
   911  func f2() { panic("XXX") }
   912  `
   913  
   914  func TestTrampoline(t *testing.T) {
   915  	// Test that trampoline insertion works as expected.
   916  	// For stress test, we set -debugtramp=2 flag, which sets a very low
   917  	// threshold for trampoline generation, and essentially all cross-package
   918  	// calls will use trampolines.
   919  	buildmodes := []string{"default"}
   920  	switch runtime.GOARCH {
   921  	case "arm", "arm64", "ppc64", "loong64":
   922  	case "ppc64le":
   923  		// Trampolines are generated differently when internal linking PIE, test them too.
   924  		buildmodes = append(buildmodes, "pie")
   925  	default:
   926  		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
   927  	}
   928  
   929  	testenv.MustHaveGoBuild(t)
   930  
   931  	t.Parallel()
   932  
   933  	tmpdir := t.TempDir()
   934  
   935  	src := filepath.Join(tmpdir, "hello.go")
   936  	err := os.WriteFile(src, []byte(testTrampSrc), 0666)
   937  	if err != nil {
   938  		t.Fatal(err)
   939  	}
   940  	exe := filepath.Join(tmpdir, "hello.exe")
   941  
   942  	for _, mode := range buildmodes {
   943  		cmd := goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
   944  		out, err := cmd.CombinedOutput()
   945  		if err != nil {
   946  			t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
   947  		}
   948  		cmd = testenv.Command(t, exe)
   949  		out, err = cmd.CombinedOutput()
   950  		if err != nil {
   951  			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
   952  		}
   953  		if string(out) != "hello\n" {
   954  			t.Errorf("unexpected output (%s):\n%s", mode, out)
   955  		}
   956  
   957  		out, err = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe).CombinedOutput()
   958  		if err != nil {
   959  			t.Errorf("nm failure: %s\n%s\n", err, string(out))
   960  		}
   961  		if ok, _ := regexp.Match("T runtime.deferreturn(\\+0)?-tramp0", out); !ok {
   962  			t.Errorf("Trampoline T runtime.deferreturn(+0)?-tramp0 is missing")
   963  		}
   964  	}
   965  }
   966  
   967  const testTrampCgoSrc = `
   968  package main
   969  
   970  // #include <stdio.h>
   971  // void CHello() { printf("hello\n"); fflush(stdout); }
   972  import "C"
   973  
   974  func main() {
   975  	C.CHello()
   976  }
   977  `
   978  
   979  func TestTrampolineCgo(t *testing.T) {
   980  	// Test that trampoline insertion works for cgo code.
   981  	// For stress test, we set -debugtramp=2 flag, which sets a very low
   982  	// threshold for trampoline generation, and essentially all cross-package
   983  	// calls will use trampolines.
   984  	buildmodes := []string{"default"}
   985  	switch runtime.GOARCH {
   986  	case "arm", "arm64", "ppc64", "loong64":
   987  	case "ppc64le":
   988  		// Trampolines are generated differently when internal linking PIE, test them too.
   989  		buildmodes = append(buildmodes, "pie")
   990  	default:
   991  		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
   992  	}
   993  
   994  	testenv.MustHaveGoBuild(t)
   995  	testenv.MustHaveCGO(t)
   996  
   997  	t.Parallel()
   998  
   999  	tmpdir := t.TempDir()
  1000  
  1001  	src := filepath.Join(tmpdir, "hello.go")
  1002  	err := os.WriteFile(src, []byte(testTrampCgoSrc), 0666)
  1003  	if err != nil {
  1004  		t.Fatal(err)
  1005  	}
  1006  	exe := filepath.Join(tmpdir, "hello.exe")
  1007  
  1008  	for _, mode := range buildmodes {
  1009  		cmd := goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
  1010  		out, err := cmd.CombinedOutput()
  1011  		if err != nil {
  1012  			t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
  1013  		}
  1014  		cmd = testenv.Command(t, exe)
  1015  		out, err = cmd.CombinedOutput()
  1016  		if err != nil {
  1017  			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
  1018  		}
  1019  		if string(out) != "hello\n" && string(out) != "hello\r\n" {
  1020  			t.Errorf("unexpected output (%s):\n%s", mode, out)
  1021  		}
  1022  
  1023  		// Test internal linking mode.
  1024  
  1025  		if !testenv.CanInternalLink(true) {
  1026  			continue
  1027  		}
  1028  		cmd = goCmd(t, "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
  1029  		out, err = cmd.CombinedOutput()
  1030  		if err != nil {
  1031  			t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
  1032  		}
  1033  		cmd = testenv.Command(t, exe)
  1034  		out, err = cmd.CombinedOutput()
  1035  		if err != nil {
  1036  			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
  1037  		}
  1038  		if string(out) != "hello\n" && string(out) != "hello\r\n" {
  1039  			t.Errorf("unexpected output (%s):\n%s", mode, out)
  1040  		}
  1041  	}
  1042  }
  1043  
  1044  func TestIndexMismatch(t *testing.T) {
  1045  	// Test that index mismatch will cause a link-time error (not run-time error).
  1046  	// This shouldn't happen with "go build". We invoke the compiler and the linker
  1047  	// manually, and try to "trick" the linker with an inconsistent object file.
  1048  	testenv.MustHaveGoBuild(t)
  1049  	// N.B. the build below explictly doesn't pass through
  1050  	// -asan/-msan/-race, so we don't care about those.
  1051  	testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
  1052  
  1053  	t.Parallel()
  1054  
  1055  	tmpdir := t.TempDir()
  1056  
  1057  	aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")
  1058  	bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")
  1059  	mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")
  1060  	aObj := filepath.Join(tmpdir, "a.o")
  1061  	mObj := filepath.Join(tmpdir, "main.o")
  1062  	exe := filepath.Join(tmpdir, "main.exe")
  1063  
  1064  	importcfgFile := filepath.Join(tmpdir, "runtime.importcfg")
  1065  	testenv.WriteImportcfg(t, importcfgFile, nil, "runtime")
  1066  	importcfgWithAFile := filepath.Join(tmpdir, "witha.importcfg")
  1067  	testenv.WriteImportcfg(t, importcfgWithAFile, map[string]string{"a": aObj}, "runtime")
  1068  
  1069  	// Build a program with main package importing package a.
  1070  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, aSrc)
  1071  	t.Log(cmd)
  1072  	out, err := cmd.CombinedOutput()
  1073  	if err != nil {
  1074  		t.Fatalf("compiling a.go failed: %v\n%s", err, out)
  1075  	}
  1076  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgWithAFile, "-p=main", "-I", tmpdir, "-o", mObj, mSrc)
  1077  	t.Log(cmd)
  1078  	out, err = cmd.CombinedOutput()
  1079  	if err != nil {
  1080  		t.Fatalf("compiling main.go failed: %v\n%s", err, out)
  1081  	}
  1082  	cmd = linkCmd(t, "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
  1083  	t.Log(cmd)
  1084  	out, err = cmd.CombinedOutput()
  1085  	if err != nil {
  1086  		if runtime.GOOS == "android" && runtime.GOARCH == "arm64" {
  1087  			testenv.SkipFlaky(t, 58806)
  1088  		}
  1089  		t.Errorf("linking failed: %v\n%s", err, out)
  1090  	}
  1091  
  1092  	// Now, overwrite a.o with the object of b.go. This should
  1093  	// result in an index mismatch.
  1094  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgFile, "-p=a", "-o", aObj, bSrc)
  1095  	t.Log(cmd)
  1096  	out, err = cmd.CombinedOutput()
  1097  	if err != nil {
  1098  		t.Fatalf("compiling a.go failed: %v\n%s", err, out)
  1099  	}
  1100  	cmd = linkCmd(t, "-importcfg="+importcfgWithAFile, "-L", tmpdir, "-o", exe, mObj)
  1101  	t.Log(cmd)
  1102  	out, err = cmd.CombinedOutput()
  1103  	if err == nil {
  1104  		t.Fatalf("linking didn't fail")
  1105  	}
  1106  	if !bytes.Contains(out, []byte("fingerprint mismatch")) {
  1107  		t.Errorf("did not see expected error message. out:\n%s", out)
  1108  	}
  1109  }
  1110  
  1111  func TestPErsrcBinutils(t *testing.T) {
  1112  	// Test that PE rsrc section is handled correctly (issue 39658).
  1113  	testenv.MustHaveGoBuild(t)
  1114  
  1115  	if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" {
  1116  		// This test is limited to amd64 and 386, because binutils is limited as such
  1117  		t.Skipf("this is only for windows/amd64 and windows/386")
  1118  	}
  1119  
  1120  	t.Parallel()
  1121  
  1122  	tmpdir := t.TempDir()
  1123  
  1124  	pkgdir := filepath.Join("testdata", "pe-binutils")
  1125  	exe := filepath.Join(tmpdir, "a.exe")
  1126  	cmd := goCmd(t, "build", "-o", exe)
  1127  	cmd.Dir = pkgdir
  1128  	// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
  1129  	out, err := cmd.CombinedOutput()
  1130  	if err != nil {
  1131  		t.Fatalf("building failed: %v, output:\n%s", err, out)
  1132  	}
  1133  
  1134  	// Check that the binary contains the rsrc data
  1135  	b, err := os.ReadFile(exe)
  1136  	if err != nil {
  1137  		t.Fatalf("reading output failed: %v", err)
  1138  	}
  1139  	if !bytes.Contains(b, []byte("Hello Gophers!")) {
  1140  		t.Fatalf("binary does not contain expected content")
  1141  	}
  1142  }
  1143  
  1144  func TestPErsrcLLVM(t *testing.T) {
  1145  	// Test that PE rsrc section is handled correctly (issue 39658).
  1146  	testenv.MustHaveGoBuild(t)
  1147  
  1148  	if runtime.GOOS != "windows" {
  1149  		t.Skipf("this is a windows-only test")
  1150  	}
  1151  
  1152  	t.Parallel()
  1153  
  1154  	tmpdir := t.TempDir()
  1155  
  1156  	pkgdir := filepath.Join("testdata", "pe-llvm")
  1157  	exe := filepath.Join(tmpdir, "a.exe")
  1158  	cmd := goCmd(t, "build", "-o", exe)
  1159  	cmd.Dir = pkgdir
  1160  	// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
  1161  	out, err := cmd.CombinedOutput()
  1162  	if err != nil {
  1163  		t.Fatalf("building failed: %v, output:\n%s", err, out)
  1164  	}
  1165  
  1166  	// Check that the binary contains the rsrc data
  1167  	b, err := os.ReadFile(exe)
  1168  	if err != nil {
  1169  		t.Fatalf("reading output failed: %v", err)
  1170  	}
  1171  	if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
  1172  		t.Fatalf("binary does not contain expected content")
  1173  	}
  1174  }
  1175  
  1176  func TestContentAddressableSymbols(t *testing.T) {
  1177  	// Test that the linker handles content-addressable symbols correctly.
  1178  	testenv.MustHaveGoBuild(t)
  1179  
  1180  	t.Parallel()
  1181  
  1182  	src := filepath.Join("testdata", "testHashedSyms", "p.go")
  1183  	cmd := goCmd(t, "run", src)
  1184  	out, err := cmd.CombinedOutput()
  1185  	if err != nil {
  1186  		t.Errorf("command %s failed: %v\n%s", cmd, err, out)
  1187  	}
  1188  }
  1189  
  1190  func TestReadOnly(t *testing.T) {
  1191  	// Test that read-only data is indeed read-only.
  1192  	testenv.MustHaveGoBuild(t)
  1193  
  1194  	t.Parallel()
  1195  
  1196  	src := filepath.Join("testdata", "testRO", "x.go")
  1197  	cmd := goCmd(t, "run", src)
  1198  	out, err := cmd.CombinedOutput()
  1199  	if err == nil {
  1200  		t.Errorf("running test program did not fail. output:\n%s", out)
  1201  	}
  1202  }
  1203  
  1204  const testIssue38554Src = `
  1205  package main
  1206  
  1207  type T [10<<20]byte
  1208  
  1209  //go:noinline
  1210  func f() T {
  1211  	return T{} // compiler will make a large stmp symbol, but not used.
  1212  }
  1213  
  1214  func main() {
  1215  	x := f()
  1216  	println(x[1])
  1217  }
  1218  `
  1219  
  1220  func TestIssue38554(t *testing.T) {
  1221  	testenv.MustHaveGoBuild(t)
  1222  
  1223  	t.Parallel()
  1224  
  1225  	tmpdir := t.TempDir()
  1226  
  1227  	src := filepath.Join(tmpdir, "x.go")
  1228  	err := os.WriteFile(src, []byte(testIssue38554Src), 0666)
  1229  	if err != nil {
  1230  		t.Fatalf("failed to write source file: %v", err)
  1231  	}
  1232  	exe := filepath.Join(tmpdir, "x.exe")
  1233  	cmd := goCmd(t, "build", "-o", exe, src)
  1234  	out, err := cmd.CombinedOutput()
  1235  	if err != nil {
  1236  		t.Fatalf("build failed: %v\n%s", err, out)
  1237  	}
  1238  
  1239  	fi, err := os.Stat(exe)
  1240  	if err != nil {
  1241  		t.Fatalf("failed to stat output file: %v", err)
  1242  	}
  1243  
  1244  	// The test program is not much different from a helloworld, which is
  1245  	// typically a little over 1 MB. We allow 5 MB. If the bad stmp is live,
  1246  	// it will be over 10 MB.
  1247  	const want = 5 << 20
  1248  	if got := fi.Size(); got > want {
  1249  		t.Errorf("binary too big: got %d, want < %d", got, want)
  1250  	}
  1251  }
  1252  
  1253  const testIssue42396src = `
  1254  package main
  1255  
  1256  //go:noinline
  1257  //go:nosplit
  1258  func callee(x int) {
  1259  }
  1260  
  1261  func main() {
  1262  	callee(9)
  1263  }
  1264  `
  1265  
  1266  func TestIssue42396(t *testing.T) {
  1267  	testenv.MustHaveGoBuild(t)
  1268  
  1269  	if !platform.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
  1270  		t.Skip("no race detector support")
  1271  	}
  1272  
  1273  	t.Parallel()
  1274  
  1275  	tmpdir := t.TempDir()
  1276  
  1277  	src := filepath.Join(tmpdir, "main.go")
  1278  	err := os.WriteFile(src, []byte(testIssue42396src), 0666)
  1279  	if err != nil {
  1280  		t.Fatalf("failed to write source file: %v", err)
  1281  	}
  1282  	exe := filepath.Join(tmpdir, "main.exe")
  1283  	cmd := goCmd(t, "build", "-gcflags=-race", "-o", exe, src)
  1284  	out, err := cmd.CombinedOutput()
  1285  	if err == nil {
  1286  		t.Fatalf("build unexpectedly succeeded")
  1287  	}
  1288  
  1289  	// Check to make sure that we see a reasonable error message
  1290  	// and not a panic.
  1291  	if strings.Contains(string(out), "panic:") {
  1292  		t.Fatalf("build should not fail with panic:\n%s", out)
  1293  	}
  1294  	const want = "reference to undefined builtin"
  1295  	if !strings.Contains(string(out), want) {
  1296  		t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
  1297  	}
  1298  }
  1299  
  1300  const testLargeRelocSrc = `
  1301  package main
  1302  
  1303  var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
  1304  
  1305  var addr = [...]*byte{
  1306  	&x[1<<23-1],
  1307  	&x[1<<23],
  1308  	&x[1<<23+1],
  1309  	&x[1<<24-1],
  1310  	&x[1<<24],
  1311  	&x[1<<24+1],
  1312  }
  1313  
  1314  func main() {
  1315  	// check relocations in instructions
  1316  	check(x[1<<23-1], 0)
  1317  	check(x[1<<23], 23)
  1318  	check(x[1<<23+1], 0)
  1319  	check(x[1<<24-1], 0)
  1320  	check(x[1<<24], 24)
  1321  	check(x[1<<24+1], 0)
  1322  
  1323  	// check absolute address relocations in data
  1324  	check(*addr[0], 0)
  1325  	check(*addr[1], 23)
  1326  	check(*addr[2], 0)
  1327  	check(*addr[3], 0)
  1328  	check(*addr[4], 24)
  1329  	check(*addr[5], 0)
  1330  }
  1331  
  1332  func check(x, y byte) {
  1333  	if x != y {
  1334  		panic("FAIL")
  1335  	}
  1336  }
  1337  `
  1338  
  1339  func TestLargeReloc(t *testing.T) {
  1340  	// Test that large relocation addend is handled correctly.
  1341  	// In particular, on darwin/arm64 when external linking,
  1342  	// Mach-O relocation has only 24-bit addend. See issue #42738.
  1343  	testenv.MustHaveGoBuild(t)
  1344  	t.Parallel()
  1345  
  1346  	tmpdir := t.TempDir()
  1347  
  1348  	src := filepath.Join(tmpdir, "x.go")
  1349  	err := os.WriteFile(src, []byte(testLargeRelocSrc), 0666)
  1350  	if err != nil {
  1351  		t.Fatalf("failed to write source file: %v", err)
  1352  	}
  1353  	cmd := goCmd(t, "run", src)
  1354  	out, err := cmd.CombinedOutput()
  1355  	if err != nil {
  1356  		t.Errorf("build failed: %v. output:\n%s", err, out)
  1357  	}
  1358  
  1359  	if testenv.HasCGO() { // currently all targets that support cgo can external link
  1360  		cmd = goCmd(t, "run", "-ldflags=-linkmode=external", src)
  1361  		out, err = cmd.CombinedOutput()
  1362  		if err != nil {
  1363  			t.Fatalf("build failed: %v. output:\n%s", err, out)
  1364  		}
  1365  	}
  1366  }
  1367  
  1368  func TestUnlinkableObj(t *testing.T) {
  1369  	// Test that the linker emits an error with unlinkable object.
  1370  	testenv.MustHaveGoBuild(t)
  1371  	t.Parallel()
  1372  
  1373  	if true /* was buildcfg.Experiment.Unified */ {
  1374  		t.Skip("TODO(mdempsky): Fix ICE when importing unlinkable objects for GOEXPERIMENT=unified")
  1375  	}
  1376  
  1377  	tmpdir := t.TempDir()
  1378  
  1379  	xSrc := filepath.Join(tmpdir, "x.go")
  1380  	pSrc := filepath.Join(tmpdir, "p.go")
  1381  	xObj := filepath.Join(tmpdir, "x.o")
  1382  	pObj := filepath.Join(tmpdir, "p.o")
  1383  	exe := filepath.Join(tmpdir, "x.exe")
  1384  	importcfgfile := filepath.Join(tmpdir, "importcfg")
  1385  	testenv.WriteImportcfg(t, importcfgfile, map[string]string{"p": pObj})
  1386  	err := os.WriteFile(xSrc, []byte("package main\nimport _ \"p\"\nfunc main() {}\n"), 0666)
  1387  	if err != nil {
  1388  		t.Fatalf("failed to write source file: %v", err)
  1389  	}
  1390  	err = os.WriteFile(pSrc, []byte("package p\n"), 0666)
  1391  	if err != nil {
  1392  		t.Fatalf("failed to write source file: %v", err)
  1393  	}
  1394  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", pObj, pSrc) // without -p
  1395  	out, err := cmd.CombinedOutput()
  1396  	if err != nil {
  1397  		t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
  1398  	}
  1399  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=main", "-o", xObj, xSrc)
  1400  	out, err = cmd.CombinedOutput()
  1401  	if err != nil {
  1402  		t.Fatalf("compile x.go failed: %v. output:\n%s", err, out)
  1403  	}
  1404  	cmd = linkCmd(t, "-importcfg="+importcfgfile, "-o", exe, xObj)
  1405  	out, err = cmd.CombinedOutput()
  1406  	if err == nil {
  1407  		t.Fatalf("link did not fail")
  1408  	}
  1409  	if !bytes.Contains(out, []byte("unlinkable object")) {
  1410  		t.Errorf("did not see expected error message. out:\n%s", out)
  1411  	}
  1412  
  1413  	// It is okay to omit -p for (only) main package.
  1414  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-p=p", "-o", pObj, pSrc)
  1415  	out, err = cmd.CombinedOutput()
  1416  	if err != nil {
  1417  		t.Fatalf("compile p.go failed: %v. output:\n%s", err, out)
  1418  	}
  1419  	cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-importcfg="+importcfgfile, "-o", xObj, xSrc) // without -p
  1420  	out, err = cmd.CombinedOutput()
  1421  	if err != nil {
  1422  		t.Fatalf("compile failed: %v. output:\n%s", err, out)
  1423  	}
  1424  
  1425  	cmd = linkCmd(t, "-importcfg="+importcfgfile, "-o", exe, xObj)
  1426  	out, err = cmd.CombinedOutput()
  1427  	if err != nil {
  1428  		t.Errorf("link failed: %v. output:\n%s", err, out)
  1429  	}
  1430  }
  1431  
  1432  func TestExtLinkCmdlineDeterminism(t *testing.T) {
  1433  	// Test that we pass flags in deterministic order to the external linker
  1434  	testenv.MustHaveGoBuild(t)
  1435  	testenv.MustHaveCGO(t) // this test requires -linkmode=external
  1436  	t.Parallel()
  1437  
  1438  	// test source code, with some cgo exports
  1439  	testSrc := `
  1440  package main
  1441  import "C"
  1442  //export F1
  1443  func F1() {}
  1444  //export F2
  1445  func F2() {}
  1446  //export F3
  1447  func F3() {}
  1448  func main() {}
  1449  `
  1450  
  1451  	tmpdir := t.TempDir()
  1452  	src := filepath.Join(tmpdir, "x.go")
  1453  	if err := os.WriteFile(src, []byte(testSrc), 0666); err != nil {
  1454  		t.Fatal(err)
  1455  	}
  1456  	exe := filepath.Join(tmpdir, "x.exe")
  1457  
  1458  	// Use a deterministic tmp directory so the temporary file paths are
  1459  	// deterministic.
  1460  	linktmp := filepath.Join(tmpdir, "linktmp")
  1461  	if err := os.Mkdir(linktmp, 0777); err != nil {
  1462  		t.Fatal(err)
  1463  	}
  1464  
  1465  	// Link with -v -linkmode=external to see the flags we pass to the
  1466  	// external linker.
  1467  	ldflags := "-ldflags=-v -linkmode=external -tmpdir=" + linktmp
  1468  	var out0 []byte
  1469  	for i := 0; i < 5; i++ {
  1470  		cmd := goCmd(t, "build", ldflags, "-o", exe, src)
  1471  		out, err := cmd.CombinedOutput()
  1472  		if err != nil {
  1473  			t.Fatalf("build failed: %v, output:\n%s", err, out)
  1474  		}
  1475  		if err := os.Remove(exe); err != nil {
  1476  			t.Fatal(err)
  1477  		}
  1478  
  1479  		// extract the "host link" invocation
  1480  		j := bytes.Index(out, []byte("\nhost link:"))
  1481  		if j == -1 {
  1482  			t.Fatalf("host link step not found, output:\n%s", out)
  1483  		}
  1484  		out = out[j+1:]
  1485  		k := bytes.Index(out, []byte("\n"))
  1486  		if k == -1 {
  1487  			t.Fatalf("no newline after host link, output:\n%s", out)
  1488  		}
  1489  		out = out[:k]
  1490  
  1491  		// filter out output file name, which is passed by the go
  1492  		// command and is nondeterministic.
  1493  		fs := bytes.Fields(out)
  1494  		for i, f := range fs {
  1495  			if bytes.Equal(f, []byte(`"-o"`)) && i+1 < len(fs) {
  1496  				fs[i+1] = []byte("a.out")
  1497  				break
  1498  			}
  1499  		}
  1500  		out = bytes.Join(fs, []byte{' '})
  1501  
  1502  		if i == 0 {
  1503  			out0 = out
  1504  			continue
  1505  		}
  1506  		if !bytes.Equal(out0, out) {
  1507  			t.Fatalf("output differ:\n%s\n==========\n%s", out0, out)
  1508  		}
  1509  	}
  1510  }
  1511  
  1512  // TestResponseFile tests that creating a response file to pass to the
  1513  // external linker works correctly.
  1514  func TestResponseFile(t *testing.T) {
  1515  	t.Parallel()
  1516  
  1517  	testenv.MustHaveGoBuild(t)
  1518  
  1519  	// This test requires -linkmode=external. Currently all
  1520  	// systems that support cgo support -linkmode=external.
  1521  	testenv.MustHaveCGO(t)
  1522  
  1523  	tmpdir := t.TempDir()
  1524  
  1525  	src := filepath.Join(tmpdir, "x.go")
  1526  	if err := os.WriteFile(src, []byte(`package main; import "C"; func main() {}`), 0666); err != nil {
  1527  		t.Fatal(err)
  1528  	}
  1529  
  1530  	// We don't use goCmd here, as -toolexec doesn't use response files.
  1531  	// This test is more for the go command than the linker anyhow.
  1532  
  1533  	cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-o", "output", "x.go")
  1534  	cmd.Dir = tmpdir
  1535  
  1536  	// Add enough arguments to push cmd/link into creating a response file.
  1537  	var sb strings.Builder
  1538  	sb.WriteString(`'-ldflags=all="-extldflags=`)
  1539  	for i := 0; i < sys.ExecArgLengthLimit/len("-g"); i++ {
  1540  		if i > 0 {
  1541  			sb.WriteString(" ")
  1542  		}
  1543  		sb.WriteString("-g")
  1544  	}
  1545  	sb.WriteString(`"'`)
  1546  	cmd = testenv.CleanCmdEnv(cmd)
  1547  	cmd.Env = append(cmd.Env, "GOFLAGS="+sb.String())
  1548  
  1549  	out, err := cmd.CombinedOutput()
  1550  	if len(out) > 0 {
  1551  		t.Logf("%s", out)
  1552  	}
  1553  	if err != nil {
  1554  		t.Error(err)
  1555  	}
  1556  }
  1557  
  1558  func TestDynimportVar(t *testing.T) {
  1559  	// Test that we can access dynamically imported variables.
  1560  	// Currently darwin only.
  1561  	if runtime.GOOS != "darwin" {
  1562  		t.Skip("skip on non-darwin platform")
  1563  	}
  1564  
  1565  	testenv.MustHaveGoBuild(t)
  1566  	testenv.MustHaveCGO(t)
  1567  
  1568  	t.Parallel()
  1569  
  1570  	tmpdir := t.TempDir()
  1571  	exe := filepath.Join(tmpdir, "a.exe")
  1572  	src := filepath.Join("testdata", "dynimportvar", "main.go")
  1573  
  1574  	for _, mode := range []string{"internal", "external"} {
  1575  		cmd := goCmd(t, "build", "-ldflags=-linkmode="+mode, "-o", exe, src)
  1576  		out, err := cmd.CombinedOutput()
  1577  		if err != nil {
  1578  			t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
  1579  		}
  1580  		cmd = testenv.Command(t, exe)
  1581  		out, err = cmd.CombinedOutput()
  1582  		if err != nil {
  1583  			t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
  1584  		}
  1585  	}
  1586  }
  1587  
  1588  const helloSrc = `
  1589  package main
  1590  var X = 42
  1591  var Y int
  1592  func main() { println("hello", X, Y) }
  1593  `
  1594  
  1595  func TestFlagS(t *testing.T) {
  1596  	// Test that the -s flag strips the symbol table.
  1597  	testenv.MustHaveGoBuild(t)
  1598  
  1599  	t.Parallel()
  1600  
  1601  	tmpdir := t.TempDir()
  1602  	exe := filepath.Join(tmpdir, "a.exe")
  1603  	src := filepath.Join(tmpdir, "a.go")
  1604  	err := os.WriteFile(src, []byte(helloSrc), 0666)
  1605  	if err != nil {
  1606  		t.Fatal(err)
  1607  	}
  1608  
  1609  	modes := []string{"auto"}
  1610  	if testenv.HasCGO() {
  1611  		modes = append(modes, "external")
  1612  	}
  1613  
  1614  	// check a text symbol, a data symbol, and a BSS symbol
  1615  	syms := []string{"main.main", "main.X", "main.Y"}
  1616  
  1617  	for _, mode := range modes {
  1618  		cmd := goCmd(t, "build", "-ldflags=-s -linkmode="+mode, "-o", exe, src)
  1619  		out, err := cmd.CombinedOutput()
  1620  		if err != nil {
  1621  			t.Fatalf("build (linkmode=%s) failed: %v\n%s", mode, err, out)
  1622  		}
  1623  		cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
  1624  		out, err = cmd.CombinedOutput()
  1625  		if err != nil {
  1626  			if _, ok := errors.AsType[*exec.ExitError](err); !ok {
  1627  				// Error exit is fine as it may have no symbols.
  1628  				// On darwin we need to emit dynamic symbol references so it
  1629  				// actually has some symbols, and nm succeeds.
  1630  				t.Errorf("(mode=%s) go tool nm failed: %v\n%s", mode, err, out)
  1631  			}
  1632  		}
  1633  		for _, s := range syms {
  1634  			if bytes.Contains(out, []byte(s)) {
  1635  				t.Errorf("(mode=%s): unexpected symbol %s", mode, s)
  1636  			}
  1637  		}
  1638  	}
  1639  }
  1640  
  1641  func TestRandLayout(t *testing.T) {
  1642  	// Test that the -randlayout flag randomizes function order and
  1643  	// generates a working binary.
  1644  	testenv.MustHaveGoBuild(t)
  1645  
  1646  	t.Parallel()
  1647  
  1648  	tmpdir := t.TempDir()
  1649  
  1650  	src := filepath.Join(tmpdir, "hello.go")
  1651  	err := os.WriteFile(src, []byte(trivialSrc), 0666)
  1652  	if err != nil {
  1653  		t.Fatal(err)
  1654  	}
  1655  
  1656  	var syms [2]string
  1657  	for i, seed := range []string{"123", "456"} {
  1658  		exe := filepath.Join(tmpdir, "hello"+seed+".exe")
  1659  		cmd := goCmd(t, "build", "-ldflags=-randlayout="+seed, "-o", exe, src)
  1660  		out, err := cmd.CombinedOutput()
  1661  		if err != nil {
  1662  			t.Fatalf("seed=%v: build failed: %v\n%s", seed, err, out)
  1663  		}
  1664  		cmd = testenv.Command(t, exe)
  1665  		err = cmd.Run()
  1666  		if err != nil {
  1667  			t.Fatalf("seed=%v: executable failed to run: %v\n%s", seed, err, out)
  1668  		}
  1669  		cmd = testenv.Command(t, testenv.GoToolPath(t), "tool", "nm", exe)
  1670  		out, err = cmd.CombinedOutput()
  1671  		if err != nil {
  1672  			t.Fatalf("seed=%v: fail to run \"go tool nm\": %v\n%s", seed, err, out)
  1673  		}
  1674  		syms[i] = string(out)
  1675  	}
  1676  	if syms[0] == syms[1] {
  1677  		t.Errorf("randlayout with different seeds produced same layout:\n%s\n===\n\n%s", syms[0], syms[1])
  1678  	}
  1679  }
  1680  
  1681  func TestCheckLinkname(t *testing.T) {
  1682  	// Test that code containing blocked linknames does not build.
  1683  	testenv.MustHaveGoBuild(t)
  1684  	t.Parallel()
  1685  
  1686  	tmpdir := t.TempDir()
  1687  
  1688  	tests := []struct {
  1689  		src string
  1690  		ok  bool
  1691  	}{
  1692  		// use (instantiation) of public API is ok
  1693  		{"ok.go", true},
  1694  		// push linkname is ok
  1695  		{"push.go", true},
  1696  		// using a linknamed variable to reference an assembly
  1697  		// function in the same package is ok
  1698  		{"textvar", true},
  1699  		// pull linkname of blocked symbol is not ok
  1700  		{"coro.go", false},
  1701  		{"coro_var.go", false},
  1702  		// assembly reference is not ok
  1703  		{"coro_asm", false},
  1704  		// pull-only linkname is not ok
  1705  		{"coro2.go", false},
  1706  		// pull linkname of a builtin symbol is not ok
  1707  		{"builtin.go", false},
  1708  		{"addmoduledata.go", false},
  1709  		{"freegc.go", false},
  1710  		// legacy bad linkname is ok, for now
  1711  		{"fastrand.go", true},
  1712  		{"badlinkname.go", true},
  1713  	}
  1714  	for _, test := range tests {
  1715  		test := test
  1716  		t.Run(test.src, func(t *testing.T) {
  1717  			t.Parallel()
  1718  			src := "./testdata/linkname/" + test.src
  1719  			exe := filepath.Join(tmpdir, test.src+".exe")
  1720  			cmd := goCmd(t, "build", "-o", exe, src)
  1721  			out, err := cmd.CombinedOutput()
  1722  			if test.ok && err != nil {
  1723  				t.Errorf("build failed unexpectedly: %v:\n%s", err, out)
  1724  			}
  1725  			if !test.ok && err == nil {
  1726  				t.Errorf("build succeeded unexpectedly: %v:\n%s", err, out)
  1727  			}
  1728  		})
  1729  	}
  1730  }
  1731  
  1732  func TestLinknameBSS(t *testing.T) {
  1733  	// Test that the linker chooses the right one as the definition
  1734  	// for linknamed variables. See issue #72032.
  1735  	testenv.MustHaveGoBuild(t)
  1736  	t.Parallel()
  1737  
  1738  	tmpdir := t.TempDir()
  1739  
  1740  	src := filepath.Join("testdata", "linkname", "sched.go")
  1741  	exe := filepath.Join(tmpdir, "sched.exe")
  1742  	cmd := goCmd(t, "build", "-o", exe, src)
  1743  	out, err := cmd.CombinedOutput()
  1744  	if err != nil {
  1745  		t.Fatalf("build failed unexpectedly: %v:\n%s", err, out)
  1746  	}
  1747  
  1748  	// Check the symbol size.
  1749  	f, err := objfile.Open(exe)
  1750  	if err != nil {
  1751  		t.Fatalf("fail to open executable: %v", err)
  1752  	}
  1753  	defer f.Close()
  1754  	syms, err := f.Symbols()
  1755  	if err != nil {
  1756  		t.Fatalf("fail to get symbols: %v", err)
  1757  	}
  1758  	found := false
  1759  	for _, s := range syms {
  1760  		if s.Name == "runtime.sched" || s.Name == "_runtime.sched" {
  1761  			found = true
  1762  			if s.Size < 100 {
  1763  				// As of Go 1.25 (Mar 2025), runtime.sched has 6848 bytes on
  1764  				// darwin/arm64. It should always be larger than 100 bytes on
  1765  				// all platforms.
  1766  				t.Errorf("runtime.sched symbol size too small: want > 100, got %d", s.Size)
  1767  			}
  1768  		}
  1769  	}
  1770  	if !found {
  1771  		t.Errorf("runtime.sched symbol not found")
  1772  	}
  1773  
  1774  	// Executable should run.
  1775  	cmd = testenv.Command(t, exe)
  1776  	out, err = cmd.CombinedOutput()
  1777  	if err != nil {
  1778  		t.Errorf("executable failed to run: %v\n%s", err, out)
  1779  	}
  1780  }
  1781  
  1782  // setValueFromBytes copies from a []byte to a variable.
  1783  // This is used to get correctly aligned values in TestFuncdataPlacement.
  1784  func setValueFromBytes[T any](p *T, s []byte) {
  1785  	copy(unsafe.Slice((*byte)(unsafe.Pointer(p)), unsafe.Sizeof(*p)), s)
  1786  }
  1787  
  1788  // Test that all funcdata values are stored in the .gopclntab section.
  1789  // This is pretty ugly as there is no API for accessing this data.
  1790  // This test will have to be updated when the data formats change.
  1791  func TestFuncdataPlacement(t *testing.T) {
  1792  	testenv.MustHaveGoBuild(t)
  1793  	t.Parallel()
  1794  
  1795  	tmpdir := t.TempDir()
  1796  	src := filepath.Join(tmpdir, "x.go")
  1797  	if err := os.WriteFile(src, []byte(trivialSrc), 0o444); err != nil {
  1798  		t.Fatal(err)
  1799  	}
  1800  
  1801  	exe := filepath.Join(tmpdir, "x.exe")
  1802  	cmd := goCmd(t, "build", "-o", exe, src)
  1803  	if out, err := cmd.CombinedOutput(); err != nil {
  1804  		t.Fatalf("build failed; %v, output:\n%s", err, out)
  1805  	}
  1806  
  1807  	// We want to find the funcdata in the executable.
  1808  	// We look at the section table to find the .gopclntab section,
  1809  	// which starts with the pcHeader.
  1810  	// That will give us the table of functions,
  1811  	// which we can use to find the funcdata.
  1812  
  1813  	ef, _ := elf.Open(exe)
  1814  	mf, _ := macho.Open(exe)
  1815  	pf, _ := pe.Open(exe)
  1816  	xf, _ := xcoff.Open(exe)
  1817  	// TODO: plan9
  1818  	if ef == nil && mf == nil && pf == nil && xf == nil {
  1819  		t.Skip("unrecognized executable file format")
  1820  	}
  1821  
  1822  	const moddataSymName = "runtime.firstmoduledata"
  1823  	const gofuncSymName = "go:func.*"
  1824  	var (
  1825  		pclntab      []byte
  1826  		pclntabAddr  uint64
  1827  		pclntabEnd   uint64
  1828  		moddataAddr  uint64
  1829  		moddataBytes []byte
  1830  		gofuncAddr   uint64
  1831  		imageBase    uint64
  1832  	)
  1833  	switch {
  1834  	case ef != nil:
  1835  		defer ef.Close()
  1836  
  1837  		syms, err := ef.Symbols()
  1838  		if err != nil {
  1839  			t.Fatal(err)
  1840  		}
  1841  		for _, sym := range syms {
  1842  			switch sym.Name {
  1843  			case moddataSymName:
  1844  				moddataAddr = sym.Value
  1845  			case gofuncSymName:
  1846  				gofuncAddr = sym.Value
  1847  			}
  1848  		}
  1849  
  1850  		for _, sec := range ef.Sections {
  1851  			if sec.Name == ".gopclntab" {
  1852  				data, err := sec.Data()
  1853  				if err != nil {
  1854  					t.Fatal(err)
  1855  				}
  1856  				pclntab = data
  1857  				pclntabAddr = sec.Addr
  1858  				pclntabEnd = sec.Addr + sec.Size
  1859  			}
  1860  			if sec.Flags&elf.SHF_ALLOC != 0 && moddataAddr >= sec.Addr && moddataAddr < sec.Addr+sec.Size {
  1861  				data, err := sec.Data()
  1862  				if err != nil {
  1863  					t.Fatal(err)
  1864  				}
  1865  				moddataBytes = data[moddataAddr-sec.Addr:]
  1866  			}
  1867  		}
  1868  
  1869  	case mf != nil:
  1870  		defer mf.Close()
  1871  
  1872  		for _, sym := range mf.Symtab.Syms {
  1873  			switch sym.Name {
  1874  			case moddataSymName:
  1875  				moddataAddr = sym.Value
  1876  			case gofuncSymName:
  1877  				gofuncAddr = sym.Value
  1878  			}
  1879  		}
  1880  
  1881  		for _, sec := range mf.Sections {
  1882  			if sec.Name == "__gopclntab" {
  1883  				data, err := sec.Data()
  1884  				if err != nil {
  1885  					t.Fatal(err)
  1886  				}
  1887  				pclntab = data
  1888  				pclntabAddr = sec.Addr
  1889  				pclntabEnd = sec.Addr + sec.Size
  1890  			}
  1891  			if moddataAddr >= sec.Addr && moddataAddr < sec.Addr+sec.Size {
  1892  				data, err := sec.Data()
  1893  				if err != nil {
  1894  					t.Fatal(err)
  1895  				}
  1896  				moddataBytes = data[moddataAddr-sec.Addr:]
  1897  			}
  1898  		}
  1899  
  1900  	case pf != nil:
  1901  		defer pf.Close()
  1902  
  1903  		switch ohdr := pf.OptionalHeader.(type) {
  1904  		case *pe.OptionalHeader32:
  1905  			imageBase = uint64(ohdr.ImageBase)
  1906  		case *pe.OptionalHeader64:
  1907  			imageBase = ohdr.ImageBase
  1908  		}
  1909  
  1910  		var moddataSym, gofuncSym, pclntabSym, epclntabSym *pe.Symbol
  1911  		for _, sym := range pf.Symbols {
  1912  			switch sym.Name {
  1913  			case moddataSymName:
  1914  				moddataSym = sym
  1915  			case gofuncSymName:
  1916  				gofuncSym = sym
  1917  			case "runtime.pclntab":
  1918  				pclntabSym = sym
  1919  			case "runtime.epclntab":
  1920  				epclntabSym = sym
  1921  			}
  1922  		}
  1923  
  1924  		if moddataSym == nil {
  1925  			t.Fatalf("could not find symbol %s", moddataSymName)
  1926  		}
  1927  		if gofuncSym == nil {
  1928  			t.Fatalf("could not find symbol %s", gofuncSymName)
  1929  		}
  1930  		if pclntabSym == nil {
  1931  			t.Fatal("could not find symbol runtime.pclntab")
  1932  		}
  1933  		if epclntabSym == nil {
  1934  			t.Fatal("could not find symbol runtime.epclntab")
  1935  		}
  1936  
  1937  		sec := pf.Sections[moddataSym.SectionNumber-1]
  1938  		data, err := sec.Data()
  1939  		if err != nil {
  1940  			t.Fatal(err)
  1941  		}
  1942  		moddataBytes = data[moddataSym.Value:]
  1943  		moddataAddr = uint64(sec.VirtualAddress + moddataSym.Value)
  1944  
  1945  		sec = pf.Sections[gofuncSym.SectionNumber-1]
  1946  		gofuncAddr = uint64(sec.VirtualAddress + gofuncSym.Value)
  1947  
  1948  		if pclntabSym.SectionNumber != epclntabSym.SectionNumber {
  1949  			t.Fatalf("runtime.pclntab section %d != runtime.epclntab section %d", pclntabSym.SectionNumber, epclntabSym.SectionNumber)
  1950  		}
  1951  		sec = pf.Sections[pclntabSym.SectionNumber-1]
  1952  		data, err = sec.Data()
  1953  		if err != nil {
  1954  			t.Fatal(err)
  1955  		}
  1956  		pclntab = data[pclntabSym.Value:epclntabSym.Value]
  1957  		pclntabAddr = uint64(sec.VirtualAddress + pclntabSym.Value)
  1958  		pclntabEnd = uint64(sec.VirtualAddress + epclntabSym.Value)
  1959  
  1960  	case xf != nil:
  1961  		defer xf.Close()
  1962  
  1963  		var moddataSym, gofuncSym, pclntabSym, epclntabSym *xcoff.Symbol
  1964  		for _, sym := range xf.Symbols {
  1965  			switch sym.Name {
  1966  			case moddataSymName:
  1967  				moddataSym = sym
  1968  			case gofuncSymName:
  1969  				gofuncSym = sym
  1970  			case "runtime.pclntab":
  1971  				pclntabSym = sym
  1972  			case "runtime.epclntab":
  1973  				epclntabSym = sym
  1974  			}
  1975  		}
  1976  
  1977  		if moddataSym == nil {
  1978  			t.Fatalf("could not find symbol %s", moddataSymName)
  1979  		}
  1980  		if gofuncSym == nil {
  1981  			t.Fatalf("could not find symbol %s", gofuncSymName)
  1982  		}
  1983  		if pclntabSym == nil {
  1984  			t.Fatal("could not find symbol runtime.pclntab")
  1985  		}
  1986  		if epclntabSym == nil {
  1987  			t.Fatal("could not find symbol runtime.epclntab")
  1988  		}
  1989  
  1990  		sec := xf.Sections[moddataSym.SectionNumber-1]
  1991  		data, err := sec.Data()
  1992  		if err != nil {
  1993  			t.Fatal(err)
  1994  		}
  1995  		moddataBytes = data[moddataSym.Value:]
  1996  		moddataAddr = uint64(sec.VirtualAddress + moddataSym.Value)
  1997  
  1998  		sec = xf.Sections[gofuncSym.SectionNumber-1]
  1999  		gofuncAddr = uint64(sec.VirtualAddress + gofuncSym.Value)
  2000  
  2001  		if pclntabSym.SectionNumber != epclntabSym.SectionNumber {
  2002  			t.Fatalf("runtime.pclntab section %d != runtime.epclntab section %d", pclntabSym.SectionNumber, epclntabSym.SectionNumber)
  2003  		}
  2004  		sec = xf.Sections[pclntabSym.SectionNumber-1]
  2005  		data, err = sec.Data()
  2006  		if err != nil {
  2007  			t.Fatal(err)
  2008  		}
  2009  		pclntab = data[pclntabSym.Value:epclntabSym.Value]
  2010  		pclntabAddr = uint64(sec.VirtualAddress + pclntabSym.Value)
  2011  		pclntabEnd = uint64(sec.VirtualAddress + epclntabSym.Value)
  2012  
  2013  	default:
  2014  		panic("can't happen")
  2015  	}
  2016  
  2017  	if len(pclntab) == 0 {
  2018  		t.Fatal("could not find pclntab section")
  2019  	}
  2020  	if moddataAddr == 0 {
  2021  		t.Fatalf("could not find %s symbol", moddataSymName)
  2022  	}
  2023  	if gofuncAddr == 0 {
  2024  		t.Fatalf("could not find %s symbol", gofuncSymName)
  2025  	}
  2026  	if gofuncAddr < pclntabAddr || gofuncAddr >= pclntabEnd {
  2027  		t.Fatalf("%s out of range: value %#x not between %#x and %#x", gofuncSymName, gofuncAddr, pclntabAddr, pclntabEnd)
  2028  	}
  2029  	if len(moddataBytes) == 0 {
  2030  		t.Fatal("could not find module data")
  2031  	}
  2032  
  2033  	// What a slice looks like in the object file.
  2034  	type moddataSlice struct {
  2035  		addr uintptr
  2036  		len  int
  2037  		cap  int
  2038  	}
  2039  
  2040  	// This needs to match the struct defined in runtime/symtab.go,
  2041  	// and written out by (*Link).symtab.
  2042  	// This is not the complete moddata struct, only what we need here.
  2043  	type moddataType struct {
  2044  		pcHeader     uintptr
  2045  		funcnametab  moddataSlice
  2046  		cutab        moddataSlice
  2047  		filetab      moddataSlice
  2048  		pctab        moddataSlice
  2049  		pclntable    moddataSlice
  2050  		ftab         moddataSlice
  2051  		findfunctab  uintptr
  2052  		minpc, maxpc uintptr
  2053  
  2054  		text, etext           uintptr
  2055  		noptrdata, enoptrdata uintptr
  2056  		data, edata           uintptr
  2057  		bss, ebss             uintptr
  2058  		noptrbss, enoptrbss   uintptr
  2059  		covctrs, ecovctrs     uintptr
  2060  		end, gcdata, gcbss    uintptr
  2061  		types, etypes         uintptr
  2062  		rodata                uintptr
  2063  		gofunc                uintptr
  2064  	}
  2065  
  2066  	// The executable is on the same system as we are running,
  2067  	// so the sizes and alignments should match.
  2068  	// But moddataBytes itself may not be aligned as needed.
  2069  	// Copy to a variable to ensure alignment.
  2070  	var moddata moddataType
  2071  	setValueFromBytes(&moddata, moddataBytes)
  2072  
  2073  	ftabAddr := uint64(moddata.ftab.addr) - imageBase
  2074  	if ftabAddr < pclntabAddr || ftabAddr >= pclntabEnd {
  2075  		t.Fatalf("ftab address %#x not between %#x and %#x", ftabAddr, pclntabAddr, pclntabEnd)
  2076  	}
  2077  
  2078  	// From runtime/symtab.go and the linker function writePCToFunc.
  2079  	type functab struct {
  2080  		entryoff uint32
  2081  		funcoff  uint32
  2082  	}
  2083  	// The ftab slice in moddata has one extra entry used to record
  2084  	// the final PC.
  2085  	ftabLen := moddata.ftab.len - 1
  2086  	ftab := make([]functab, ftabLen)
  2087  	copy(ftab, unsafe.Slice((*functab)(unsafe.Pointer(&pclntab[ftabAddr-pclntabAddr])), ftabLen))
  2088  
  2089  	ftabBase := uint64(moddata.pclntable.addr) - imageBase
  2090  
  2091  	// From runtime/runtime2.go _func and the linker function writeFuncs.
  2092  	type funcEntry struct {
  2093  		entryOff uint32
  2094  		nameOff  int32
  2095  
  2096  		args        int32
  2097  		deferreturn uint32
  2098  
  2099  		pcsp      uint32
  2100  		pcfile    uint32
  2101  		pcln      uint32
  2102  		npcdata   uint32
  2103  		cuOffset  uint32
  2104  		startLine int32
  2105  		funcID    abi.FuncID
  2106  		flag      abi.FuncFlag
  2107  		_         [1]byte
  2108  		nfuncdata uint8
  2109  	}
  2110  
  2111  	for i, ftabEntry := range ftab {
  2112  		funcAddr := ftabBase + uint64(ftabEntry.funcoff)
  2113  		if funcAddr < pclntabAddr || funcAddr >= pclntabEnd {
  2114  			t.Errorf("ftab entry %d address %#x not between %#x and %#x", i, funcAddr, pclntabAddr, pclntabEnd)
  2115  			continue
  2116  		}
  2117  
  2118  		var fe funcEntry
  2119  		setValueFromBytes(&fe, pclntab[funcAddr-pclntabAddr:])
  2120  
  2121  		funcdataVals := funcAddr + uint64(unsafe.Sizeof(fe)) + uint64(fe.npcdata*4)
  2122  		for j := range fe.nfuncdata {
  2123  			var funcdataVal uint32
  2124  			setValueFromBytes(&funcdataVal, pclntab[funcdataVals+uint64(j)*4-pclntabAddr:])
  2125  			if funcdataVal == ^uint32(0) {
  2126  				continue
  2127  			}
  2128  			funcdataAddr := gofuncAddr + uint64(funcdataVal)
  2129  			if funcdataAddr < pclntabAddr || funcdataAddr >= pclntabEnd {
  2130  				t.Errorf("ftab entry %d funcdata %d address %#x not between %#x and %#x", i, j, funcdataAddr, pclntabAddr, pclntabEnd)
  2131  			}
  2132  		}
  2133  	}
  2134  
  2135  	if uint64(moddata.findfunctab)-imageBase < pclntabAddr || uint64(moddata.findfunctab)-imageBase >= pclntabEnd {
  2136  		t.Errorf("findfunctab address %#x not between %#x and %#x", moddata.findfunctab, pclntabAddr, pclntabEnd)
  2137  	}
  2138  }
  2139  
  2140  // Test that moduledata winds up in its own .go.module section.
  2141  func TestModuledataPlacement(t *testing.T) {
  2142  	testenv.MustHaveGoBuild(t)
  2143  	t.Parallel()
  2144  
  2145  	tmpdir := t.TempDir()
  2146  	src := filepath.Join(tmpdir, "x.go")
  2147  	if err := os.WriteFile(src, []byte(trivialSrc), 0o444); err != nil {
  2148  		t.Fatal(err)
  2149  	}
  2150  
  2151  	exe := filepath.Join(tmpdir, "x.exe")
  2152  	cmd := goCmd(t, "build", "-o", exe, src)
  2153  	if out, err := cmd.CombinedOutput(); err != nil {
  2154  		t.Fatalf("build failed; %v, output:\n%s", err, out)
  2155  	}
  2156  
  2157  	ef, _ := elf.Open(exe)
  2158  	mf, _ := macho.Open(exe)
  2159  	pf, _ := pe.Open(exe)
  2160  	xf, _ := xcoff.Open(exe)
  2161  	// TODO: plan9
  2162  	if ef == nil && mf == nil && pf == nil && xf == nil {
  2163  		t.Skip("unrecognized executable file format")
  2164  	}
  2165  
  2166  	const moddataSymName = "runtime.firstmoduledata"
  2167  	switch {
  2168  	case ef != nil:
  2169  		defer ef.Close()
  2170  
  2171  		syms, err := ef.Symbols()
  2172  		if err != nil {
  2173  			t.Fatal(err)
  2174  		}
  2175  		for _, sym := range syms {
  2176  			if sym.Name == moddataSymName {
  2177  				sec := ef.Sections[sym.Section]
  2178  				if sec.Name != ".go.module" {
  2179  					t.Errorf("moduledata in section %s, not .go.module", sec.Name)
  2180  				}
  2181  				if sym.Value != sec.Addr {
  2182  					t.Errorf("moduledata address %#x != section start address %#x", sym.Value, sec.Addr)
  2183  				}
  2184  				break
  2185  			}
  2186  		}
  2187  
  2188  	case mf != nil:
  2189  		defer mf.Close()
  2190  
  2191  		for _, sym := range mf.Symtab.Syms {
  2192  			if sym.Name == moddataSymName {
  2193  				if sym.Sect == 0 {
  2194  					t.Error("moduledata not in a section")
  2195  				} else {
  2196  					sec := mf.Sections[sym.Sect-1]
  2197  					if sec.Name != "__go_module" {
  2198  						t.Errorf("moduledata in section %s, not __go.module", sec.Name)
  2199  					}
  2200  					if sym.Value != sec.Addr {
  2201  						t.Errorf("moduledata address %#x != section start address %#x", sym.Value, sec.Addr)
  2202  					}
  2203  				}
  2204  				break
  2205  			}
  2206  		}
  2207  
  2208  	case pf != nil, xf != nil:
  2209  		if pf != nil {
  2210  			defer pf.Close()
  2211  		}
  2212  		if xf != nil {
  2213  			defer xf.Close()
  2214  		}
  2215  
  2216  		// On Windows and AIX all the Go specific sections
  2217  		// get stuffed into a few sections,
  2218  		// so there is nothing to test here.
  2219  	}
  2220  }
  2221  

View as plain text