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  	"cmd/internal/sys"
    11  	"debug/macho"
    12  	"internal/testenv"
    13  	"io/ioutil"
    14  	"os"
    15  	"os/exec"
    16  	"path/filepath"
    17  	"regexp"
    18  	"runtime"
    19  	"strings"
    20  	"testing"
    21  )
    22  
    23  var AuthorPaidByTheColumnInch struct {
    24  	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."`
    25  
    26  	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."`
    27  
    28  	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."`
    29  
    30  	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."`
    31  }
    32  
    33  func TestLargeSymName(t *testing.T) {
    34  	// The compiler generates a symbol name using the string form of the
    35  	// type. This tests that the linker can read symbol names larger than
    36  	// the bufio buffer. Issue #15104.
    37  	_ = AuthorPaidByTheColumnInch
    38  }
    39  
    40  func TestIssue21703(t *testing.T) {
    41  	t.Parallel()
    42  
    43  	testenv.MustHaveGoBuild(t)
    44  
    45  	const source = `
    46  package main
    47  const X = "\n!\n"
    48  func main() {}
    49  `
    50  
    51  	tmpdir := t.TempDir()
    52  
    53  	err := ioutil.WriteFile(filepath.Join(tmpdir, "main.go"), []byte(source), 0666)
    54  	if err != nil {
    55  		t.Fatalf("failed to write main.go: %v\n", err)
    56  	}
    57  
    58  	cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "main.go")
    59  	cmd.Dir = tmpdir
    60  	out, err := cmd.CombinedOutput()
    61  	if err != nil {
    62  		t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out)
    63  	}
    64  
    65  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "main.o")
    66  	cmd.Dir = tmpdir
    67  	out, err = cmd.CombinedOutput()
    68  	if err != nil {
    69  		t.Fatalf("failed to link main.o: %v, output: %s\n", err, out)
    70  	}
    71  }
    72  
    73  // TestIssue28429 ensures that the linker does not attempt to link
    74  // sections not named *.o. Such sections may be used by a build system
    75  // to, for example, save facts produced by a modular static analysis
    76  // such as golang.org/x/tools/go/analysis.
    77  func TestIssue28429(t *testing.T) {
    78  	t.Parallel()
    79  
    80  	testenv.MustHaveGoBuild(t)
    81  
    82  	tmpdir := t.TempDir()
    83  
    84  	write := func(name, content string) {
    85  		err := ioutil.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
    86  		if err != nil {
    87  			t.Fatal(err)
    88  		}
    89  	}
    90  
    91  	runGo := func(args ...string) {
    92  		cmd := exec.Command(testenv.GoToolPath(t), args...)
    93  		cmd.Dir = tmpdir
    94  		out, err := cmd.CombinedOutput()
    95  		if err != nil {
    96  			t.Fatalf("'go %s' failed: %v, output: %s",
    97  				strings.Join(args, " "), err, out)
    98  		}
    99  	}
   100  
   101  	// Compile a main package.
   102  	write("main.go", "package main; func main() {}")
   103  	runGo("tool", "compile", "-p", "main", "main.go")
   104  	runGo("tool", "pack", "c", "main.a", "main.o")
   105  
   106  	// Add an extra section with a short, non-.o name.
   107  	// This simulates an alternative build system.
   108  	write(".facts", "this is not an object file")
   109  	runGo("tool", "pack", "r", "main.a", ".facts")
   110  
   111  	// Verify that the linker does not attempt
   112  	// to compile the extra section.
   113  	runGo("tool", "link", "main.a")
   114  }
   115  
   116  func TestUnresolved(t *testing.T) {
   117  	testenv.MustHaveGoBuild(t)
   118  
   119  	t.Parallel()
   120  
   121  	tmpdir := t.TempDir()
   122  
   123  	write := func(name, content string) {
   124  		err := ioutil.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
   125  		if err != nil {
   126  			t.Fatal(err)
   127  		}
   128  	}
   129  
   130  	// Test various undefined references. Because of issue #29852,
   131  	// this used to give confusing error messages because the
   132  	// linker would find an undefined reference to "zero" created
   133  	// by the runtime package.
   134  
   135  	write("go.mod", "module testunresolved\n")
   136  	write("main.go", `package main
   137  
   138  func main() {
   139          x()
   140  }
   141  
   142  func x()
   143  `)
   144  	write("main.s", `
   145  TEXT ·x(SB),0,$0
   146          MOVD zero<>(SB), AX
   147          MOVD zero(SB), AX
   148          MOVD ·zero(SB), AX
   149          RET
   150  `)
   151  	cmd := exec.Command(testenv.GoToolPath(t), "build")
   152  	cmd.Dir = tmpdir
   153  	cmd.Env = append(os.Environ(),
   154  		"GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath"))
   155  	out, err := cmd.CombinedOutput()
   156  	if err == nil {
   157  		t.Fatalf("expected build to fail, but it succeeded")
   158  	}
   159  	out = regexp.MustCompile("(?m)^#.*\n").ReplaceAll(out, nil)
   160  	got := string(out)
   161  	want := `main.x: relocation target zero not defined
   162  main.x: relocation target zero not defined
   163  main.x: relocation target main.zero not defined
   164  `
   165  	if want != got {
   166  		t.Fatalf("want:\n%sgot:\n%s", want, got)
   167  	}
   168  }
   169  
   170  func TestIssue33979(t *testing.T) {
   171  	testenv.MustHaveGoBuild(t)
   172  	testenv.MustHaveCGO(t)
   173  	testenv.MustInternalLink(t)
   174  
   175  	// Skip test on platforms that do not support cgo internal linking.
   176  	switch runtime.GOARCH {
   177  	case "mips", "mipsle", "mips64", "mips64le":
   178  		t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
   179  	}
   180  	if runtime.GOOS == "aix" ||
   181  		runtime.GOOS == "windows" && runtime.GOARCH == "arm64" {
   182  		t.Skipf("Skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
   183  	}
   184  
   185  	t.Parallel()
   186  
   187  	tmpdir := t.TempDir()
   188  
   189  	write := func(name, content string) {
   190  		err := ioutil.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
   191  		if err != nil {
   192  			t.Fatal(err)
   193  		}
   194  	}
   195  
   196  	run := func(name string, args ...string) string {
   197  		cmd := exec.Command(name, args...)
   198  		cmd.Dir = tmpdir
   199  		out, err := cmd.CombinedOutput()
   200  		if err != nil {
   201  			t.Fatalf("'go %s' failed: %v, output: %s", strings.Join(args, " "), err, out)
   202  		}
   203  		return string(out)
   204  	}
   205  	runGo := func(args ...string) string {
   206  		return run(testenv.GoToolPath(t), args...)
   207  	}
   208  
   209  	// Test object with undefined reference that was not generated
   210  	// by Go, resulting in an SXREF symbol being loaded during linking.
   211  	// Because of issue #33979, the SXREF symbol would be found during
   212  	// error reporting, resulting in confusing error messages.
   213  
   214  	write("main.go", `package main
   215  func main() {
   216          x()
   217  }
   218  func x()
   219  `)
   220  	// The following assembly must work on all architectures.
   221  	write("x.s", `
   222  TEXT ·x(SB),0,$0
   223          CALL foo(SB)
   224          RET
   225  `)
   226  	write("x.c", `
   227  void undefined();
   228  
   229  void foo() {
   230          undefined();
   231  }
   232  `)
   233  
   234  	cc := strings.TrimSpace(runGo("env", "CC"))
   235  	cflags := strings.Fields(runGo("env", "GOGCCFLAGS"))
   236  
   237  	// Compile, assemble and pack the Go and C code.
   238  	runGo("tool", "asm", "-gensymabis", "-o", "symabis", "x.s")
   239  	runGo("tool", "compile", "-symabis", "symabis", "-p", "main", "-o", "x1.o", "main.go")
   240  	runGo("tool", "asm", "-o", "x2.o", "x.s")
   241  	run(cc, append(cflags, "-c", "-o", "x3.o", "x.c")...)
   242  	runGo("tool", "pack", "c", "x.a", "x1.o", "x2.o", "x3.o")
   243  
   244  	// Now attempt to link using the internal linker.
   245  	cmd := exec.Command(testenv.GoToolPath(t), "tool", "link", "-linkmode=internal", "x.a")
   246  	cmd.Dir = tmpdir
   247  	out, err := cmd.CombinedOutput()
   248  	if err == nil {
   249  		t.Fatalf("expected link to fail, but it succeeded")
   250  	}
   251  	re := regexp.MustCompile(`(?m)^main\(.*text\): relocation target undefined not defined$`)
   252  	if !re.Match(out) {
   253  		t.Fatalf("got:\n%q\nwant:\n%s", out, re)
   254  	}
   255  }
   256  
   257  func TestBuildForTvOS(t *testing.T) {
   258  	testenv.MustHaveCGO(t)
   259  	testenv.MustHaveGoBuild(t)
   260  
   261  	// Only run this on darwin/amd64, where we can cross build for tvOS.
   262  	if runtime.GOARCH != "amd64" || runtime.GOOS != "darwin" {
   263  		t.Skip("skipping on non-darwin/amd64 platform")
   264  	}
   265  	if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
   266  		t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty")
   267  	}
   268  	if err := exec.Command("xcrun", "--help").Run(); err != nil {
   269  		t.Skipf("error running xcrun, required for iOS cross build: %v", err)
   270  	}
   271  
   272  	t.Parallel()
   273  
   274  	sdkPath, err := exec.Command("xcrun", "--sdk", "appletvos", "--show-sdk-path").Output()
   275  	if err != nil {
   276  		t.Skip("failed to locate appletvos SDK, skipping")
   277  	}
   278  	CC := []string{
   279  		"clang",
   280  		"-arch",
   281  		"arm64",
   282  		"-isysroot", strings.TrimSpace(string(sdkPath)),
   283  		"-mtvos-version-min=12.0",
   284  		"-fembed-bitcode",
   285  	}
   286  	CGO_LDFLAGS := []string{"-framework", "CoreFoundation"}
   287  	lib := filepath.Join("testdata", "testBuildFortvOS", "lib.go")
   288  	tmpDir := t.TempDir()
   289  
   290  	ar := filepath.Join(tmpDir, "lib.a")
   291  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", ar, lib)
   292  	cmd.Env = append(os.Environ(),
   293  		"CGO_ENABLED=1",
   294  		"GOOS=ios",
   295  		"GOARCH=arm64",
   296  		"CC="+strings.Join(CC, " "),
   297  		"CGO_CFLAGS=", // ensure CGO_CFLAGS does not contain any flags. Issue #35459
   298  		"CGO_LDFLAGS="+strings.Join(CGO_LDFLAGS, " "),
   299  	)
   300  	if out, err := cmd.CombinedOutput(); err != nil {
   301  		t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
   302  	}
   303  
   304  	link := exec.Command(CC[0], CC[1:]...)
   305  	link.Args = append(link.Args, CGO_LDFLAGS...)
   306  	link.Args = append(link.Args, "-o", filepath.Join(tmpDir, "a.out")) // Avoid writing to package directory.
   307  	link.Args = append(link.Args, ar, filepath.Join("testdata", "testBuildFortvOS", "main.m"))
   308  	if out, err := link.CombinedOutput(); err != nil {
   309  		t.Fatalf("%v: %v:\n%s", link.Args, err, out)
   310  	}
   311  }
   312  
   313  var testXFlagSrc = `
   314  package main
   315  var X = "hello"
   316  var Z = [99999]int{99998:12345} // make it large enough to be mmaped
   317  func main() { println(X) }
   318  `
   319  
   320  func TestXFlag(t *testing.T) {
   321  	testenv.MustHaveGoBuild(t)
   322  
   323  	t.Parallel()
   324  
   325  	tmpdir := t.TempDir()
   326  
   327  	src := filepath.Join(tmpdir, "main.go")
   328  	err := ioutil.WriteFile(src, []byte(testXFlagSrc), 0666)
   329  	if err != nil {
   330  		t.Fatal(err)
   331  	}
   332  
   333  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src)
   334  	if out, err := cmd.CombinedOutput(); err != nil {
   335  		t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
   336  	}
   337  }
   338  
   339  var testMachOBuildVersionSrc = `
   340  package main
   341  func main() { }
   342  `
   343  
   344  func TestMachOBuildVersion(t *testing.T) {
   345  	testenv.MustHaveGoBuild(t)
   346  
   347  	t.Parallel()
   348  
   349  	tmpdir := t.TempDir()
   350  
   351  	src := filepath.Join(tmpdir, "main.go")
   352  	err := ioutil.WriteFile(src, []byte(testMachOBuildVersionSrc), 0666)
   353  	if err != nil {
   354  		t.Fatal(err)
   355  	}
   356  
   357  	exe := filepath.Join(tmpdir, "main")
   358  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal", "-o", exe, src)
   359  	cmd.Env = append(os.Environ(),
   360  		"CGO_ENABLED=0",
   361  		"GOOS=darwin",
   362  		"GOARCH=amd64",
   363  	)
   364  	if out, err := cmd.CombinedOutput(); err != nil {
   365  		t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
   366  	}
   367  	exef, err := os.Open(exe)
   368  	if err != nil {
   369  		t.Fatal(err)
   370  	}
   371  	defer exef.Close()
   372  	exem, err := macho.NewFile(exef)
   373  	if err != nil {
   374  		t.Fatal(err)
   375  	}
   376  	found := false
   377  	const LC_BUILD_VERSION = 0x32
   378  	checkMin := func(ver uint32) {
   379  		major, minor := (ver>>16)&0xff, (ver>>8)&0xff
   380  		if major != 10 || minor < 9 {
   381  			t.Errorf("LC_BUILD_VERSION version %d.%d < 10.9", major, minor)
   382  		}
   383  	}
   384  	for _, cmd := range exem.Loads {
   385  		raw := cmd.Raw()
   386  		type_ := exem.ByteOrder.Uint32(raw)
   387  		if type_ != LC_BUILD_VERSION {
   388  			continue
   389  		}
   390  		osVer := exem.ByteOrder.Uint32(raw[12:])
   391  		checkMin(osVer)
   392  		sdkVer := exem.ByteOrder.Uint32(raw[16:])
   393  		checkMin(sdkVer)
   394  		found = true
   395  		break
   396  	}
   397  	if !found {
   398  		t.Errorf("no LC_BUILD_VERSION load command found")
   399  	}
   400  }
   401  
   402  const Issue34788src = `
   403  
   404  package blah
   405  
   406  func Blah(i int) int {
   407  	a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
   408  	return a[i&7]
   409  }
   410  `
   411  
   412  func TestIssue34788Android386TLSSequence(t *testing.T) {
   413  	testenv.MustHaveGoBuild(t)
   414  
   415  	// This is a cross-compilation test, so it doesn't make
   416  	// sense to run it on every GOOS/GOARCH combination. Limit
   417  	// the test to amd64 + darwin/linux.
   418  	if runtime.GOARCH != "amd64" ||
   419  		(runtime.GOOS != "darwin" && runtime.GOOS != "linux") {
   420  		t.Skip("skipping on non-{linux,darwin}/amd64 platform")
   421  	}
   422  
   423  	t.Parallel()
   424  
   425  	tmpdir := t.TempDir()
   426  
   427  	src := filepath.Join(tmpdir, "blah.go")
   428  	err := ioutil.WriteFile(src, []byte(Issue34788src), 0666)
   429  	if err != nil {
   430  		t.Fatal(err)
   431  	}
   432  
   433  	obj := filepath.Join(tmpdir, "blah.o")
   434  	cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", obj, src)
   435  	cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")
   436  	if out, err := cmd.CombinedOutput(); err != nil {
   437  		t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)
   438  	}
   439  
   440  	// Run objdump on the resulting object.
   441  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "objdump", obj)
   442  	out, oerr := cmd.CombinedOutput()
   443  	if oerr != nil {
   444  		t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)
   445  	}
   446  
   447  	// Sift through the output; we should not be seeing any R_TLS_LE relocs.
   448  	scanner := bufio.NewScanner(bytes.NewReader(out))
   449  	for scanner.Scan() {
   450  		line := scanner.Text()
   451  		if strings.Contains(line, "R_TLS_LE") {
   452  			t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)
   453  		}
   454  	}
   455  }
   456  
   457  const testStrictDupGoSrc = `
   458  package main
   459  func f()
   460  func main() { f() }
   461  `
   462  
   463  const testStrictDupAsmSrc1 = `
   464  #include "textflag.h"
   465  TEXT	·f(SB), NOSPLIT|DUPOK, $0-0
   466  	RET
   467  `
   468  
   469  const testStrictDupAsmSrc2 = `
   470  #include "textflag.h"
   471  TEXT	·f(SB), NOSPLIT|DUPOK, $0-0
   472  	JMP	0(PC)
   473  `
   474  
   475  const testStrictDupAsmSrc3 = `
   476  #include "textflag.h"
   477  GLOBL ·rcon(SB), RODATA|DUPOK, $64
   478  `
   479  
   480  const testStrictDupAsmSrc4 = `
   481  #include "textflag.h"
   482  GLOBL ·rcon(SB), RODATA|DUPOK, $32
   483  `
   484  
   485  func TestStrictDup(t *testing.T) {
   486  	// Check that -strictdups flag works.
   487  	testenv.MustHaveGoBuild(t)
   488  
   489  	asmfiles := []struct {
   490  		fname   string
   491  		payload string
   492  	}{
   493  		{"a", testStrictDupAsmSrc1},
   494  		{"b", testStrictDupAsmSrc2},
   495  		{"c", testStrictDupAsmSrc3},
   496  		{"d", testStrictDupAsmSrc4},
   497  	}
   498  
   499  	t.Parallel()
   500  
   501  	tmpdir := t.TempDir()
   502  
   503  	src := filepath.Join(tmpdir, "x.go")
   504  	err := ioutil.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
   505  	if err != nil {
   506  		t.Fatal(err)
   507  	}
   508  	for _, af := range asmfiles {
   509  		src = filepath.Join(tmpdir, af.fname+".s")
   510  		err = ioutil.WriteFile(src, []byte(af.payload), 0666)
   511  		if err != nil {
   512  			t.Fatal(err)
   513  		}
   514  	}
   515  	src = filepath.Join(tmpdir, "go.mod")
   516  	err = ioutil.WriteFile(src, []byte("module teststrictdup\n"), 0666)
   517  	if err != nil {
   518  		t.Fatal(err)
   519  	}
   520  
   521  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1")
   522  	cmd.Dir = tmpdir
   523  	out, err := cmd.CombinedOutput()
   524  	if err != nil {
   525  		t.Errorf("linking with -strictdups=1 failed: %v\n%s", err, string(out))
   526  	}
   527  	if !bytes.Contains(out, []byte("mismatched payload")) {
   528  		t.Errorf("unexpected output:\n%s", out)
   529  	}
   530  
   531  	cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2")
   532  	cmd.Dir = tmpdir
   533  	out, err = cmd.CombinedOutput()
   534  	if err == nil {
   535  		t.Errorf("linking with -strictdups=2 did not fail")
   536  	}
   537  	// NB: on amd64 we get the 'new length' error, on arm64 the 'different
   538  	// contents' error.
   539  	if !(bytes.Contains(out, []byte("mismatched payload: new length")) ||
   540  		bytes.Contains(out, []byte("mismatched payload: same length but different contents"))) ||
   541  		!bytes.Contains(out, []byte("mismatched payload: different sizes")) {
   542  		t.Errorf("unexpected output:\n%s", out)
   543  	}
   544  }
   545  
   546  const testFuncAlignSrc = `
   547  package main
   548  import (
   549  	"fmt"
   550  )
   551  func alignPc()
   552  var alignPcFnAddr uintptr
   553  
   554  func main() {
   555  	if alignPcFnAddr % 512 != 0 {
   556  		fmt.Printf("expected 512 bytes alignment, got %v\n", alignPcFnAddr)
   557  	} else {
   558  		fmt.Printf("PASS")
   559  	}
   560  }
   561  `
   562  
   563  const testFuncAlignAsmSrc = `
   564  #include "textflag.h"
   565  
   566  TEXT	·alignPc(SB),NOSPLIT, $0-0
   567  	MOVD	$2, R0
   568  	PCALIGN	$512
   569  	MOVD	$3, R1
   570  	RET
   571  
   572  GLOBL	·alignPcFnAddr(SB),RODATA,$8
   573  DATA	·alignPcFnAddr(SB)/8,$·alignPc(SB)
   574  `
   575  
   576  // TestFuncAlign verifies that the address of a function can be aligned
   577  // with a specific value on arm64.
   578  func TestFuncAlign(t *testing.T) {
   579  	if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" {
   580  		t.Skip("skipping on non-linux/arm64 platform")
   581  	}
   582  	testenv.MustHaveGoBuild(t)
   583  
   584  	t.Parallel()
   585  
   586  	tmpdir := t.TempDir()
   587  
   588  	src := filepath.Join(tmpdir, "go.mod")
   589  	err := ioutil.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666)
   590  	if err != nil {
   591  		t.Fatal(err)
   592  	}
   593  	src = filepath.Join(tmpdir, "falign.go")
   594  	err = ioutil.WriteFile(src, []byte(testFuncAlignSrc), 0666)
   595  	if err != nil {
   596  		t.Fatal(err)
   597  	}
   598  	src = filepath.Join(tmpdir, "falign.s")
   599  	err = ioutil.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)
   600  	if err != nil {
   601  		t.Fatal(err)
   602  	}
   603  
   604  	// Build and run with old object file format.
   605  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "falign")
   606  	cmd.Dir = tmpdir
   607  	out, err := cmd.CombinedOutput()
   608  	if err != nil {
   609  		t.Errorf("build failed: %v", err)
   610  	}
   611  	cmd = exec.Command(tmpdir + "/falign")
   612  	out, err = cmd.CombinedOutput()
   613  	if err != nil {
   614  		t.Errorf("failed to run with err %v, output: %s", err, out)
   615  	}
   616  	if string(out) != "PASS" {
   617  		t.Errorf("unexpected output: %s\n", out)
   618  	}
   619  }
   620  
   621  const testTrampSrc = `
   622  package main
   623  import "fmt"
   624  func main() {
   625  	fmt.Println("hello")
   626  
   627  	defer func(){
   628  		if e := recover(); e == nil {
   629  			panic("did not panic")
   630  		}
   631  	}()
   632  	f1()
   633  }
   634  
   635  // Test deferreturn trampolines. See issue #39049.
   636  func f1() { defer f2() }
   637  func f2() { panic("XXX") }
   638  `
   639  
   640  func TestTrampoline(t *testing.T) {
   641  	// Test that trampoline insertion works as expected.
   642  	// For stress test, we set -debugtramp=2 flag, which sets a very low
   643  	// threshold for trampoline generation, and essentially all cross-package
   644  	// calls will use trampolines.
   645  	switch runtime.GOARCH {
   646  	case "arm", "arm64", "ppc64", "ppc64le":
   647  	default:
   648  		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
   649  	}
   650  
   651  	testenv.MustHaveGoBuild(t)
   652  
   653  	t.Parallel()
   654  
   655  	tmpdir := t.TempDir()
   656  
   657  	src := filepath.Join(tmpdir, "hello.go")
   658  	err := ioutil.WriteFile(src, []byte(testTrampSrc), 0666)
   659  	if err != nil {
   660  		t.Fatal(err)
   661  	}
   662  	exe := filepath.Join(tmpdir, "hello.exe")
   663  
   664  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
   665  	out, err := cmd.CombinedOutput()
   666  	if err != nil {
   667  		t.Fatalf("build failed: %v\n%s", err, out)
   668  	}
   669  	cmd = exec.Command(exe)
   670  	out, err = cmd.CombinedOutput()
   671  	if err != nil {
   672  		t.Errorf("executable failed to run: %v\n%s", err, out)
   673  	}
   674  	if string(out) != "hello\n" {
   675  		t.Errorf("unexpected output:\n%s", out)
   676  	}
   677  }
   678  
   679  const testTrampCgoSrc = `
   680  package main
   681  
   682  // #include <stdio.h>
   683  // void CHello() { printf("hello\n"); fflush(stdout); }
   684  import "C"
   685  
   686  func main() {
   687  	C.CHello()
   688  }
   689  `
   690  
   691  func TestTrampolineCgo(t *testing.T) {
   692  	// Test that trampoline insertion works for cgo code.
   693  	// For stress test, we set -debugtramp=2 flag, which sets a very low
   694  	// threshold for trampoline generation, and essentially all cross-package
   695  	// calls will use trampolines.
   696  	switch runtime.GOARCH {
   697  	case "arm", "arm64", "ppc64", "ppc64le":
   698  	default:
   699  		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
   700  	}
   701  
   702  	testenv.MustHaveGoBuild(t)
   703  	testenv.MustHaveCGO(t)
   704  
   705  	t.Parallel()
   706  
   707  	tmpdir := t.TempDir()
   708  
   709  	src := filepath.Join(tmpdir, "hello.go")
   710  	err := ioutil.WriteFile(src, []byte(testTrampCgoSrc), 0666)
   711  	if err != nil {
   712  		t.Fatal(err)
   713  	}
   714  	exe := filepath.Join(tmpdir, "hello.exe")
   715  
   716  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
   717  	out, err := cmd.CombinedOutput()
   718  	if err != nil {
   719  		t.Fatalf("build failed: %v\n%s", err, out)
   720  	}
   721  	cmd = exec.Command(exe)
   722  	out, err = cmd.CombinedOutput()
   723  	if err != nil {
   724  		t.Errorf("executable failed to run: %v\n%s", err, out)
   725  	}
   726  	if string(out) != "hello\n" && string(out) != "hello\r\n" {
   727  		t.Errorf("unexpected output:\n%s", out)
   728  	}
   729  
   730  	// Test internal linking mode.
   731  
   732  	if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || (runtime.GOARCH == "arm64" && runtime.GOOS == "windows") || !testenv.CanInternalLink() {
   733  		return // internal linking cgo is not supported
   734  	}
   735  	cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
   736  	out, err = cmd.CombinedOutput()
   737  	if err != nil {
   738  		t.Fatalf("build failed: %v\n%s", err, out)
   739  	}
   740  	cmd = exec.Command(exe)
   741  	out, err = cmd.CombinedOutput()
   742  	if err != nil {
   743  		t.Errorf("executable failed to run: %v\n%s", err, out)
   744  	}
   745  	if string(out) != "hello\n" && string(out) != "hello\r\n" {
   746  		t.Errorf("unexpected output:\n%s", out)
   747  	}
   748  }
   749  
   750  func TestIndexMismatch(t *testing.T) {
   751  	// Test that index mismatch will cause a link-time error (not run-time error).
   752  	// This shouldn't happen with "go build". We invoke the compiler and the linker
   753  	// manually, and try to "trick" the linker with an inconsistent object file.
   754  	testenv.MustHaveGoBuild(t)
   755  
   756  	t.Parallel()
   757  
   758  	tmpdir := t.TempDir()
   759  
   760  	aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")
   761  	bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")
   762  	mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")
   763  	aObj := filepath.Join(tmpdir, "a.o")
   764  	mObj := filepath.Join(tmpdir, "main.o")
   765  	exe := filepath.Join(tmpdir, "main.exe")
   766  
   767  	// Build a program with main package importing package a.
   768  	cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", aObj, aSrc)
   769  	t.Log(cmd)
   770  	out, err := cmd.CombinedOutput()
   771  	if err != nil {
   772  		t.Fatalf("compiling a.go failed: %v\n%s", err, out)
   773  	}
   774  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-I", tmpdir, "-o", mObj, mSrc)
   775  	t.Log(cmd)
   776  	out, err = cmd.CombinedOutput()
   777  	if err != nil {
   778  		t.Fatalf("compiling main.go failed: %v\n%s", err, out)
   779  	}
   780  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, mObj)
   781  	t.Log(cmd)
   782  	out, err = cmd.CombinedOutput()
   783  	if err != nil {
   784  		t.Errorf("linking failed: %v\n%s", err, out)
   785  	}
   786  
   787  	// Now, overwrite a.o with the object of b.go. This should
   788  	// result in an index mismatch.
   789  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", aObj, bSrc)
   790  	t.Log(cmd)
   791  	out, err = cmd.CombinedOutput()
   792  	if err != nil {
   793  		t.Fatalf("compiling a.go failed: %v\n%s", err, out)
   794  	}
   795  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, mObj)
   796  	t.Log(cmd)
   797  	out, err = cmd.CombinedOutput()
   798  	if err == nil {
   799  		t.Fatalf("linking didn't fail")
   800  	}
   801  	if !bytes.Contains(out, []byte("fingerprint mismatch")) {
   802  		t.Errorf("did not see expected error message. out:\n%s", out)
   803  	}
   804  }
   805  
   806  func TestPErsrcBinutils(t *testing.T) {
   807  	// Test that PE rsrc section is handled correctly (issue 39658).
   808  	testenv.MustHaveGoBuild(t)
   809  
   810  	if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" {
   811  		// This test is limited to amd64 and 386, because binutils is limited as such
   812  		t.Skipf("this is only for windows/amd64 and windows/386")
   813  	}
   814  
   815  	t.Parallel()
   816  
   817  	tmpdir := t.TempDir()
   818  
   819  	pkgdir := filepath.Join("testdata", "pe-binutils")
   820  	exe := filepath.Join(tmpdir, "a.exe")
   821  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
   822  	cmd.Dir = pkgdir
   823  	// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
   824  	out, err := cmd.CombinedOutput()
   825  	if err != nil {
   826  		t.Fatalf("building failed: %v, output:\n%s", err, out)
   827  	}
   828  
   829  	// Check that the binary contains the rsrc data
   830  	b, err := ioutil.ReadFile(exe)
   831  	if err != nil {
   832  		t.Fatalf("reading output failed: %v", err)
   833  	}
   834  	if !bytes.Contains(b, []byte("Hello Gophers!")) {
   835  		t.Fatalf("binary does not contain expected content")
   836  	}
   837  }
   838  
   839  func TestPErsrcLLVM(t *testing.T) {
   840  	// Test that PE rsrc section is handled correctly (issue 39658).
   841  	testenv.MustHaveGoBuild(t)
   842  
   843  	if runtime.GOOS != "windows" {
   844  		t.Skipf("this is a windows-only test")
   845  	}
   846  
   847  	t.Parallel()
   848  
   849  	tmpdir := t.TempDir()
   850  
   851  	pkgdir := filepath.Join("testdata", "pe-llvm")
   852  	exe := filepath.Join(tmpdir, "a.exe")
   853  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
   854  	cmd.Dir = pkgdir
   855  	// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
   856  	out, err := cmd.CombinedOutput()
   857  	if err != nil {
   858  		t.Fatalf("building failed: %v, output:\n%s", err, out)
   859  	}
   860  
   861  	// Check that the binary contains the rsrc data
   862  	b, err := ioutil.ReadFile(exe)
   863  	if err != nil {
   864  		t.Fatalf("reading output failed: %v", err)
   865  	}
   866  	if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
   867  		t.Fatalf("binary does not contain expected content")
   868  	}
   869  }
   870  
   871  func TestContentAddressableSymbols(t *testing.T) {
   872  	// Test that the linker handles content-addressable symbols correctly.
   873  	testenv.MustHaveGoBuild(t)
   874  
   875  	t.Parallel()
   876  
   877  	src := filepath.Join("testdata", "testHashedSyms", "p.go")
   878  	cmd := exec.Command(testenv.GoToolPath(t), "run", src)
   879  	out, err := cmd.CombinedOutput()
   880  	if err != nil {
   881  		t.Errorf("command %s failed: %v\n%s", cmd, err, out)
   882  	}
   883  }
   884  
   885  func TestReadOnly(t *testing.T) {
   886  	// Test that read-only data is indeed read-only.
   887  	testenv.MustHaveGoBuild(t)
   888  
   889  	t.Parallel()
   890  
   891  	src := filepath.Join("testdata", "testRO", "x.go")
   892  	cmd := exec.Command(testenv.GoToolPath(t), "run", src)
   893  	out, err := cmd.CombinedOutput()
   894  	if err == nil {
   895  		t.Errorf("running test program did not fail. output:\n%s", out)
   896  	}
   897  }
   898  
   899  const testIssue38554Src = `
   900  package main
   901  
   902  type T [10<<20]byte
   903  
   904  //go:noinline
   905  func f() T {
   906  	return T{} // compiler will make a large stmp symbol, but not used.
   907  }
   908  
   909  func main() {
   910  	x := f()
   911  	println(x[1])
   912  }
   913  `
   914  
   915  func TestIssue38554(t *testing.T) {
   916  	testenv.MustHaveGoBuild(t)
   917  
   918  	t.Parallel()
   919  
   920  	tmpdir := t.TempDir()
   921  
   922  	src := filepath.Join(tmpdir, "x.go")
   923  	err := ioutil.WriteFile(src, []byte(testIssue38554Src), 0666)
   924  	if err != nil {
   925  		t.Fatalf("failed to write source file: %v", err)
   926  	}
   927  	exe := filepath.Join(tmpdir, "x.exe")
   928  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, src)
   929  	out, err := cmd.CombinedOutput()
   930  	if err != nil {
   931  		t.Fatalf("build failed: %v\n%s", err, out)
   932  	}
   933  
   934  	fi, err := os.Stat(exe)
   935  	if err != nil {
   936  		t.Fatalf("failed to stat output file: %v", err)
   937  	}
   938  
   939  	// The test program is not much different from a helloworld, which is
   940  	// typically a little over 1 MB. We allow 5 MB. If the bad stmp is live,
   941  	// it will be over 10 MB.
   942  	const want = 5 << 20
   943  	if got := fi.Size(); got > want {
   944  		t.Errorf("binary too big: got %d, want < %d", got, want)
   945  	}
   946  }
   947  
   948  const testIssue42396src = `
   949  package main
   950  
   951  //go:noinline
   952  //go:nosplit
   953  func callee(x int) {
   954  }
   955  
   956  func main() {
   957  	callee(9)
   958  }
   959  `
   960  
   961  func TestIssue42396(t *testing.T) {
   962  	testenv.MustHaveGoBuild(t)
   963  
   964  	if !sys.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
   965  		t.Skip("no race detector support")
   966  	}
   967  
   968  	t.Parallel()
   969  
   970  	tmpdir := t.TempDir()
   971  
   972  	src := filepath.Join(tmpdir, "main.go")
   973  	err := ioutil.WriteFile(src, []byte(testIssue42396src), 0666)
   974  	if err != nil {
   975  		t.Fatalf("failed to write source file: %v", err)
   976  	}
   977  	exe := filepath.Join(tmpdir, "main.exe")
   978  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-race", "-o", exe, src)
   979  	out, err := cmd.CombinedOutput()
   980  	if err == nil {
   981  		t.Fatalf("build unexpectedly succeeded")
   982  	}
   983  
   984  	// Check to make sure that we see a reasonable error message
   985  	// and not a panic.
   986  	if strings.Contains(string(out), "panic:") {
   987  		t.Fatalf("build should not fail with panic:\n%s", out)
   988  	}
   989  	const want = "reference to undefined builtin"
   990  	if !strings.Contains(string(out), want) {
   991  		t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
   992  	}
   993  }
   994  
   995  const testLargeRelocSrc = `
   996  package main
   997  
   998  var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
   999  
  1000  var addr = [...]*byte{
  1001  	&x[1<<23-1],
  1002  	&x[1<<23],
  1003  	&x[1<<23+1],
  1004  	&x[1<<24-1],
  1005  	&x[1<<24],
  1006  	&x[1<<24+1],
  1007  }
  1008  
  1009  func main() {
  1010  	// check relocations in instructions
  1011  	check(x[1<<23-1], 0)
  1012  	check(x[1<<23], 23)
  1013  	check(x[1<<23+1], 0)
  1014  	check(x[1<<24-1], 0)
  1015  	check(x[1<<24], 24)
  1016  	check(x[1<<24+1], 0)
  1017  
  1018  	// check absolute address relocations in data
  1019  	check(*addr[0], 0)
  1020  	check(*addr[1], 23)
  1021  	check(*addr[2], 0)
  1022  	check(*addr[3], 0)
  1023  	check(*addr[4], 24)
  1024  	check(*addr[5], 0)
  1025  }
  1026  
  1027  func check(x, y byte) {
  1028  	if x != y {
  1029  		panic("FAIL")
  1030  	}
  1031  }
  1032  `
  1033  
  1034  func TestLargeReloc(t *testing.T) {
  1035  	// Test that large relocation addend is handled correctly.
  1036  	// In particular, on darwin/arm64 when external linking,
  1037  	// Mach-O relocation has only 24-bit addend. See issue #42738.
  1038  	testenv.MustHaveGoBuild(t)
  1039  	t.Parallel()
  1040  
  1041  	tmpdir := t.TempDir()
  1042  
  1043  	src := filepath.Join(tmpdir, "x.go")
  1044  	err := ioutil.WriteFile(src, []byte(testLargeRelocSrc), 0666)
  1045  	if err != nil {
  1046  		t.Fatalf("failed to write source file: %v", err)
  1047  	}
  1048  	cmd := exec.Command(testenv.GoToolPath(t), "run", src)
  1049  	out, err := cmd.CombinedOutput()
  1050  	if err != nil {
  1051  		t.Errorf("build failed: %v. output:\n%s", err, out)
  1052  	}
  1053  
  1054  	if testenv.HasCGO() { // currently all targets that support cgo can external link
  1055  		cmd = exec.Command(testenv.GoToolPath(t), "run", "-ldflags=-linkmode=external", src)
  1056  		out, err = cmd.CombinedOutput()
  1057  		if err != nil {
  1058  			t.Fatalf("build failed: %v. output:\n%s", err, out)
  1059  		}
  1060  	}
  1061  }
  1062  

View as plain text