...

Source file src/cmd/link/link_test.go

Documentation: cmd/link

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

View as plain text