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

View as plain text