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

View as plain text