Black Lives Matter. Support the Equal Justice Initiative.

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  	"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  		"-framework", "CoreFoundation",
   286  	}
   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  	)
   299  	if out, err := cmd.CombinedOutput(); err != nil {
   300  		t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
   301  	}
   302  
   303  	link := exec.Command(CC[0], CC[1:]...)
   304  	link.Args = append(link.Args, "-o", filepath.Join(tmpDir, "a.out")) // Avoid writing to package directory.
   305  	link.Args = append(link.Args, ar, filepath.Join("testdata", "testBuildFortvOS", "main.m"))
   306  	if out, err := link.CombinedOutput(); err != nil {
   307  		t.Fatalf("%v: %v:\n%s", link.Args, err, out)
   308  	}
   309  }
   310  
   311  var testXFlagSrc = `
   312  package main
   313  var X = "hello"
   314  var Z = [99999]int{99998:12345} // make it large enough to be mmaped
   315  func main() { println(X) }
   316  `
   317  
   318  func TestXFlag(t *testing.T) {
   319  	testenv.MustHaveGoBuild(t)
   320  
   321  	t.Parallel()
   322  
   323  	tmpdir := t.TempDir()
   324  
   325  	src := filepath.Join(tmpdir, "main.go")
   326  	err := ioutil.WriteFile(src, []byte(testXFlagSrc), 0666)
   327  	if err != nil {
   328  		t.Fatal(err)
   329  	}
   330  
   331  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src)
   332  	if out, err := cmd.CombinedOutput(); err != nil {
   333  		t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
   334  	}
   335  }
   336  
   337  var testMachOBuildVersionSrc = `
   338  package main
   339  func main() { }
   340  `
   341  
   342  func TestMachOBuildVersion(t *testing.T) {
   343  	testenv.MustHaveGoBuild(t)
   344  
   345  	t.Parallel()
   346  
   347  	tmpdir := t.TempDir()
   348  
   349  	src := filepath.Join(tmpdir, "main.go")
   350  	err := ioutil.WriteFile(src, []byte(testMachOBuildVersionSrc), 0666)
   351  	if err != nil {
   352  		t.Fatal(err)
   353  	}
   354  
   355  	exe := filepath.Join(tmpdir, "main")
   356  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal", "-o", exe, src)
   357  	cmd.Env = append(os.Environ(),
   358  		"CGO_ENABLED=0",
   359  		"GOOS=darwin",
   360  		"GOARCH=amd64",
   361  	)
   362  	if out, err := cmd.CombinedOutput(); err != nil {
   363  		t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
   364  	}
   365  	exef, err := os.Open(exe)
   366  	if err != nil {
   367  		t.Fatal(err)
   368  	}
   369  	defer exef.Close()
   370  	exem, err := macho.NewFile(exef)
   371  	if err != nil {
   372  		t.Fatal(err)
   373  	}
   374  	found := false
   375  	const LC_BUILD_VERSION = 0x32
   376  	checkMin := func(ver uint32) {
   377  		major, minor := (ver>>16)&0xff, (ver>>8)&0xff
   378  		if major != 10 || minor < 9 {
   379  			t.Errorf("LC_BUILD_VERSION version %d.%d < 10.9", major, minor)
   380  		}
   381  	}
   382  	for _, cmd := range exem.Loads {
   383  		raw := cmd.Raw()
   384  		type_ := exem.ByteOrder.Uint32(raw)
   385  		if type_ != LC_BUILD_VERSION {
   386  			continue
   387  		}
   388  		osVer := exem.ByteOrder.Uint32(raw[12:])
   389  		checkMin(osVer)
   390  		sdkVer := exem.ByteOrder.Uint32(raw[16:])
   391  		checkMin(sdkVer)
   392  		found = true
   393  		break
   394  	}
   395  	if !found {
   396  		t.Errorf("no LC_BUILD_VERSION load command found")
   397  	}
   398  }
   399  
   400  const Issue34788src = `
   401  
   402  package blah
   403  
   404  func Blah(i int) int {
   405  	a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
   406  	return a[i&7]
   407  }
   408  `
   409  
   410  func TestIssue34788Android386TLSSequence(t *testing.T) {
   411  	testenv.MustHaveGoBuild(t)
   412  
   413  	// This is a cross-compilation test, so it doesn't make
   414  	// sense to run it on every GOOS/GOARCH combination. Limit
   415  	// the test to amd64 + darwin/linux.
   416  	if runtime.GOARCH != "amd64" ||
   417  		(runtime.GOOS != "darwin" && runtime.GOOS != "linux") {
   418  		t.Skip("skipping on non-{linux,darwin}/amd64 platform")
   419  	}
   420  
   421  	t.Parallel()
   422  
   423  	tmpdir := t.TempDir()
   424  
   425  	src := filepath.Join(tmpdir, "blah.go")
   426  	err := ioutil.WriteFile(src, []byte(Issue34788src), 0666)
   427  	if err != nil {
   428  		t.Fatal(err)
   429  	}
   430  
   431  	obj := filepath.Join(tmpdir, "blah.o")
   432  	cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", obj, src)
   433  	cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")
   434  	if out, err := cmd.CombinedOutput(); err != nil {
   435  		t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)
   436  	}
   437  
   438  	// Run objdump on the resulting object.
   439  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "objdump", obj)
   440  	out, oerr := cmd.CombinedOutput()
   441  	if oerr != nil {
   442  		t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)
   443  	}
   444  
   445  	// Sift through the output; we should not be seeing any R_TLS_LE relocs.
   446  	scanner := bufio.NewScanner(bytes.NewReader(out))
   447  	for scanner.Scan() {
   448  		line := scanner.Text()
   449  		if strings.Contains(line, "R_TLS_LE") {
   450  			t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)
   451  		}
   452  	}
   453  }
   454  
   455  const testStrictDupGoSrc = `
   456  package main
   457  func f()
   458  func main() { f() }
   459  `
   460  
   461  const testStrictDupAsmSrc1 = `
   462  #include "textflag.h"
   463  TEXT	·f(SB), NOSPLIT|DUPOK, $0-0
   464  	RET
   465  `
   466  
   467  const testStrictDupAsmSrc2 = `
   468  #include "textflag.h"
   469  TEXT	·f(SB), NOSPLIT|DUPOK, $0-0
   470  	JMP	0(PC)
   471  `
   472  
   473  const testStrictDupAsmSrc3 = `
   474  #include "textflag.h"
   475  GLOBL ·rcon(SB), RODATA|DUPOK, $64
   476  `
   477  
   478  const testStrictDupAsmSrc4 = `
   479  #include "textflag.h"
   480  GLOBL ·rcon(SB), RODATA|DUPOK, $32
   481  `
   482  
   483  func TestStrictDup(t *testing.T) {
   484  	// Check that -strictdups flag works.
   485  	testenv.MustHaveGoBuild(t)
   486  
   487  	asmfiles := []struct {
   488  		fname   string
   489  		payload string
   490  	}{
   491  		{"a", testStrictDupAsmSrc1},
   492  		{"b", testStrictDupAsmSrc2},
   493  		{"c", testStrictDupAsmSrc3},
   494  		{"d", testStrictDupAsmSrc4},
   495  	}
   496  
   497  	t.Parallel()
   498  
   499  	tmpdir := t.TempDir()
   500  
   501  	src := filepath.Join(tmpdir, "x.go")
   502  	err := ioutil.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
   503  	if err != nil {
   504  		t.Fatal(err)
   505  	}
   506  	for _, af := range asmfiles {
   507  		src = filepath.Join(tmpdir, af.fname+".s")
   508  		err = ioutil.WriteFile(src, []byte(af.payload), 0666)
   509  		if err != nil {
   510  			t.Fatal(err)
   511  		}
   512  	}
   513  	src = filepath.Join(tmpdir, "go.mod")
   514  	err = ioutil.WriteFile(src, []byte("module teststrictdup\n"), 0666)
   515  	if err != nil {
   516  		t.Fatal(err)
   517  	}
   518  
   519  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1")
   520  	cmd.Dir = tmpdir
   521  	out, err := cmd.CombinedOutput()
   522  	if err != nil {
   523  		t.Errorf("linking with -strictdups=1 failed: %v\n%s", err, string(out))
   524  	}
   525  	if !bytes.Contains(out, []byte("mismatched payload")) {
   526  		t.Errorf("unexpected output:\n%s", out)
   527  	}
   528  
   529  	cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2")
   530  	cmd.Dir = tmpdir
   531  	out, err = cmd.CombinedOutput()
   532  	if err == nil {
   533  		t.Errorf("linking with -strictdups=2 did not fail")
   534  	}
   535  	// NB: on amd64 we get the 'new length' error, on arm64 the 'different
   536  	// contents' error.
   537  	if !(bytes.Contains(out, []byte("mismatched payload: new length")) ||
   538  		bytes.Contains(out, []byte("mismatched payload: same length but different contents"))) ||
   539  		!bytes.Contains(out, []byte("mismatched payload: different sizes")) {
   540  		t.Errorf("unexpected output:\n%s", out)
   541  	}
   542  }
   543  
   544  const testFuncAlignSrc = `
   545  package main
   546  import (
   547  	"fmt"
   548  	"reflect"
   549  )
   550  func alignPc()
   551  
   552  func main() {
   553  	addr := reflect.ValueOf(alignPc).Pointer()
   554  	if (addr % 512) != 0 {
   555  		fmt.Printf("expected 512 bytes alignment, got %v\n", addr)
   556  	} else {
   557  		fmt.Printf("PASS")
   558  	}
   559  }
   560  `
   561  
   562  const testFuncAlignAsmSrc = `
   563  #include "textflag.h"
   564  
   565  TEXT	·alignPc(SB),NOSPLIT, $0-0
   566  	MOVD	$2, R0
   567  	PCALIGN	$512
   568  	MOVD	$3, R1
   569  	RET
   570  `
   571  
   572  // TestFuncAlign verifies that the address of a function can be aligned
   573  // with a specific value on arm64.
   574  func TestFuncAlign(t *testing.T) {
   575  	if runtime.GOARCH != "arm64" || runtime.GOOS != "linux" {
   576  		t.Skip("skipping on non-linux/arm64 platform")
   577  	}
   578  	testenv.MustHaveGoBuild(t)
   579  
   580  	t.Parallel()
   581  
   582  	tmpdir := t.TempDir()
   583  
   584  	src := filepath.Join(tmpdir, "go.mod")
   585  	err := ioutil.WriteFile(src, []byte("module cmd/link/TestFuncAlign/falign"), 0666)
   586  	if err != nil {
   587  		t.Fatal(err)
   588  	}
   589  	src = filepath.Join(tmpdir, "falign.go")
   590  	err = ioutil.WriteFile(src, []byte(testFuncAlignSrc), 0666)
   591  	if err != nil {
   592  		t.Fatal(err)
   593  	}
   594  	src = filepath.Join(tmpdir, "falign.s")
   595  	err = ioutil.WriteFile(src, []byte(testFuncAlignAsmSrc), 0666)
   596  	if err != nil {
   597  		t.Fatal(err)
   598  	}
   599  
   600  	// Build and run with old object file format.
   601  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", "falign")
   602  	cmd.Dir = tmpdir
   603  	out, err := cmd.CombinedOutput()
   604  	if err != nil {
   605  		t.Errorf("build failed: %v", err)
   606  	}
   607  	cmd = exec.Command(tmpdir + "/falign")
   608  	out, err = cmd.CombinedOutput()
   609  	if err != nil {
   610  		t.Errorf("failed to run with err %v, output: %s", err, out)
   611  	}
   612  	if string(out) != "PASS" {
   613  		t.Errorf("unexpected output: %s\n", out)
   614  	}
   615  }
   616  
   617  const testTrampSrc = `
   618  package main
   619  import "fmt"
   620  func main() {
   621  	fmt.Println("hello")
   622  
   623  	defer func(){
   624  		if e := recover(); e == nil {
   625  			panic("did not panic")
   626  		}
   627  	}()
   628  	f1()
   629  }
   630  
   631  // Test deferreturn trampolines. See issue #39049.
   632  func f1() { defer f2() }
   633  func f2() { panic("XXX") }
   634  `
   635  
   636  func TestTrampoline(t *testing.T) {
   637  	// Test that trampoline insertion works as expected.
   638  	// For stress test, we set -debugtramp=2 flag, which sets a very low
   639  	// threshold for trampoline generation, and essentially all cross-package
   640  	// calls will use trampolines.
   641  	switch runtime.GOARCH {
   642  	case "arm", "arm64", "ppc64", "ppc64le":
   643  	default:
   644  		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
   645  	}
   646  
   647  	testenv.MustHaveGoBuild(t)
   648  
   649  	t.Parallel()
   650  
   651  	tmpdir := t.TempDir()
   652  
   653  	src := filepath.Join(tmpdir, "hello.go")
   654  	err := ioutil.WriteFile(src, []byte(testTrampSrc), 0666)
   655  	if err != nil {
   656  		t.Fatal(err)
   657  	}
   658  	exe := filepath.Join(tmpdir, "hello.exe")
   659  
   660  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
   661  	out, err := cmd.CombinedOutput()
   662  	if err != nil {
   663  		t.Fatalf("build failed: %v\n%s", err, out)
   664  	}
   665  	cmd = exec.Command(exe)
   666  	out, err = cmd.CombinedOutput()
   667  	if err != nil {
   668  		t.Errorf("executable failed to run: %v\n%s", err, out)
   669  	}
   670  	if string(out) != "hello\n" {
   671  		t.Errorf("unexpected output:\n%s", out)
   672  	}
   673  }
   674  
   675  const testTrampCgoSrc = `
   676  package main
   677  
   678  // #include <stdio.h>
   679  // void CHello() { printf("hello\n"); fflush(stdout); }
   680  import "C"
   681  
   682  func main() {
   683  	C.CHello()
   684  }
   685  `
   686  
   687  func TestTrampolineCgo(t *testing.T) {
   688  	// Test that trampoline insertion works for cgo code.
   689  	// For stress test, we set -debugtramp=2 flag, which sets a very low
   690  	// threshold for trampoline generation, and essentially all cross-package
   691  	// calls will use trampolines.
   692  	switch runtime.GOARCH {
   693  	case "arm", "arm64", "ppc64", "ppc64le":
   694  	default:
   695  		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
   696  	}
   697  
   698  	testenv.MustHaveGoBuild(t)
   699  	testenv.MustHaveCGO(t)
   700  
   701  	t.Parallel()
   702  
   703  	tmpdir := t.TempDir()
   704  
   705  	src := filepath.Join(tmpdir, "hello.go")
   706  	err := ioutil.WriteFile(src, []byte(testTrampCgoSrc), 0666)
   707  	if err != nil {
   708  		t.Fatal(err)
   709  	}
   710  	exe := filepath.Join(tmpdir, "hello.exe")
   711  
   712  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
   713  	out, err := cmd.CombinedOutput()
   714  	if err != nil {
   715  		t.Fatalf("build failed: %v\n%s", err, out)
   716  	}
   717  	cmd = exec.Command(exe)
   718  	out, err = cmd.CombinedOutput()
   719  	if err != nil {
   720  		t.Errorf("executable failed to run: %v\n%s", err, out)
   721  	}
   722  	if string(out) != "hello\n" && string(out) != "hello\r\n" {
   723  		t.Errorf("unexpected output:\n%s", out)
   724  	}
   725  
   726  	// Test internal linking mode.
   727  
   728  	if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || (runtime.GOARCH == "arm64" && runtime.GOOS == "windows") || !testenv.CanInternalLink() {
   729  		return // internal linking cgo is not supported
   730  	}
   731  	cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
   732  	out, err = cmd.CombinedOutput()
   733  	if err != nil {
   734  		t.Fatalf("build failed: %v\n%s", err, out)
   735  	}
   736  	cmd = exec.Command(exe)
   737  	out, err = cmd.CombinedOutput()
   738  	if err != nil {
   739  		t.Errorf("executable failed to run: %v\n%s", err, out)
   740  	}
   741  	if string(out) != "hello\n" && string(out) != "hello\r\n" {
   742  		t.Errorf("unexpected output:\n%s", out)
   743  	}
   744  }
   745  
   746  func TestIndexMismatch(t *testing.T) {
   747  	// Test that index mismatch will cause a link-time error (not run-time error).
   748  	// This shouldn't happen with "go build". We invoke the compiler and the linker
   749  	// manually, and try to "trick" the linker with an inconsistent object file.
   750  	testenv.MustHaveGoBuild(t)
   751  
   752  	t.Parallel()
   753  
   754  	tmpdir := t.TempDir()
   755  
   756  	aSrc := filepath.Join("testdata", "testIndexMismatch", "a.go")
   757  	bSrc := filepath.Join("testdata", "testIndexMismatch", "b.go")
   758  	mSrc := filepath.Join("testdata", "testIndexMismatch", "main.go")
   759  	aObj := filepath.Join(tmpdir, "a.o")
   760  	mObj := filepath.Join(tmpdir, "main.o")
   761  	exe := filepath.Join(tmpdir, "main.exe")
   762  
   763  	// Build a program with main package importing package a.
   764  	cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", aObj, aSrc)
   765  	t.Log(cmd)
   766  	out, err := cmd.CombinedOutput()
   767  	if err != nil {
   768  		t.Fatalf("compiling a.go failed: %v\n%s", err, out)
   769  	}
   770  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-I", tmpdir, "-o", mObj, mSrc)
   771  	t.Log(cmd)
   772  	out, err = cmd.CombinedOutput()
   773  	if err != nil {
   774  		t.Fatalf("compiling main.go failed: %v\n%s", err, out)
   775  	}
   776  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, mObj)
   777  	t.Log(cmd)
   778  	out, err = cmd.CombinedOutput()
   779  	if err != nil {
   780  		t.Errorf("linking failed: %v\n%s", err, out)
   781  	}
   782  
   783  	// Now, overwrite a.o with the object of b.go. This should
   784  	// result in an index mismatch.
   785  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", aObj, bSrc)
   786  	t.Log(cmd)
   787  	out, err = cmd.CombinedOutput()
   788  	if err != nil {
   789  		t.Fatalf("compiling a.go failed: %v\n%s", err, out)
   790  	}
   791  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "-L", tmpdir, "-o", exe, mObj)
   792  	t.Log(cmd)
   793  	out, err = cmd.CombinedOutput()
   794  	if err == nil {
   795  		t.Fatalf("linking didn't fail")
   796  	}
   797  	if !bytes.Contains(out, []byte("fingerprint mismatch")) {
   798  		t.Errorf("did not see expected error message. out:\n%s", out)
   799  	}
   800  }
   801  
   802  func TestPErsrcBinutils(t *testing.T) {
   803  	// Test that PE rsrc section is handled correctly (issue 39658).
   804  	testenv.MustHaveGoBuild(t)
   805  
   806  	if (runtime.GOARCH != "386" && runtime.GOARCH != "amd64") || runtime.GOOS != "windows" {
   807  		// This test is limited to amd64 and 386, because binutils is limited as such
   808  		t.Skipf("this is only for windows/amd64 and windows/386")
   809  	}
   810  
   811  	t.Parallel()
   812  
   813  	tmpdir := t.TempDir()
   814  
   815  	pkgdir := filepath.Join("testdata", "pe-binutils")
   816  	exe := filepath.Join(tmpdir, "a.exe")
   817  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
   818  	cmd.Dir = pkgdir
   819  	// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
   820  	out, err := cmd.CombinedOutput()
   821  	if err != nil {
   822  		t.Fatalf("building failed: %v, output:\n%s", err, out)
   823  	}
   824  
   825  	// Check that the binary contains the rsrc data
   826  	b, err := ioutil.ReadFile(exe)
   827  	if err != nil {
   828  		t.Fatalf("reading output failed: %v", err)
   829  	}
   830  	if !bytes.Contains(b, []byte("Hello Gophers!")) {
   831  		t.Fatalf("binary does not contain expected content")
   832  	}
   833  }
   834  
   835  func TestPErsrcLLVM(t *testing.T) {
   836  	// Test that PE rsrc section is handled correctly (issue 39658).
   837  	testenv.MustHaveGoBuild(t)
   838  
   839  	if runtime.GOOS != "windows" {
   840  		t.Skipf("this is a windows-only test")
   841  	}
   842  
   843  	t.Parallel()
   844  
   845  	tmpdir := t.TempDir()
   846  
   847  	pkgdir := filepath.Join("testdata", "pe-llvm")
   848  	exe := filepath.Join(tmpdir, "a.exe")
   849  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe)
   850  	cmd.Dir = pkgdir
   851  	// cmd.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64") // uncomment if debugging in a cross-compiling environment
   852  	out, err := cmd.CombinedOutput()
   853  	if err != nil {
   854  		t.Fatalf("building failed: %v, output:\n%s", err, out)
   855  	}
   856  
   857  	// Check that the binary contains the rsrc data
   858  	b, err := ioutil.ReadFile(exe)
   859  	if err != nil {
   860  		t.Fatalf("reading output failed: %v", err)
   861  	}
   862  	if !bytes.Contains(b, []byte("resname RCDATA a.rc")) {
   863  		t.Fatalf("binary does not contain expected content")
   864  	}
   865  }
   866  
   867  func TestContentAddressableSymbols(t *testing.T) {
   868  	// Test that the linker handles content-addressable symbols correctly.
   869  	testenv.MustHaveGoBuild(t)
   870  
   871  	t.Parallel()
   872  
   873  	src := filepath.Join("testdata", "testHashedSyms", "p.go")
   874  	cmd := exec.Command(testenv.GoToolPath(t), "run", src)
   875  	out, err := cmd.CombinedOutput()
   876  	if err != nil {
   877  		t.Errorf("command %s failed: %v\n%s", cmd, err, out)
   878  	}
   879  }
   880  
   881  func TestReadOnly(t *testing.T) {
   882  	// Test that read-only data is indeed read-only.
   883  	testenv.MustHaveGoBuild(t)
   884  
   885  	t.Parallel()
   886  
   887  	src := filepath.Join("testdata", "testRO", "x.go")
   888  	cmd := exec.Command(testenv.GoToolPath(t), "run", src)
   889  	out, err := cmd.CombinedOutput()
   890  	if err == nil {
   891  		t.Errorf("running test program did not fail. output:\n%s", out)
   892  	}
   893  }
   894  
   895  const testIssue38554Src = `
   896  package main
   897  
   898  type T [10<<20]byte
   899  
   900  //go:noinline
   901  func f() T {
   902  	return T{} // compiler will make a large stmp symbol, but not used.
   903  }
   904  
   905  func main() {
   906  	x := f()
   907  	println(x[1])
   908  }
   909  `
   910  
   911  func TestIssue38554(t *testing.T) {
   912  	testenv.MustHaveGoBuild(t)
   913  
   914  	t.Parallel()
   915  
   916  	tmpdir := t.TempDir()
   917  
   918  	src := filepath.Join(tmpdir, "x.go")
   919  	err := ioutil.WriteFile(src, []byte(testIssue38554Src), 0666)
   920  	if err != nil {
   921  		t.Fatalf("failed to write source file: %v", err)
   922  	}
   923  	exe := filepath.Join(tmpdir, "x.exe")
   924  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, src)
   925  	out, err := cmd.CombinedOutput()
   926  	if err != nil {
   927  		t.Fatalf("build failed: %v\n%s", err, out)
   928  	}
   929  
   930  	fi, err := os.Stat(exe)
   931  	if err != nil {
   932  		t.Fatalf("failed to stat output file: %v", err)
   933  	}
   934  
   935  	// The test program is not much different from a helloworld, which is
   936  	// typically a little over 1 MB. We allow 5 MB. If the bad stmp is live,
   937  	// it will be over 10 MB.
   938  	const want = 5 << 20
   939  	if got := fi.Size(); got > want {
   940  		t.Errorf("binary too big: got %d, want < %d", got, want)
   941  	}
   942  }
   943  
   944  const testIssue42396src = `
   945  package main
   946  
   947  //go:noinline
   948  //go:nosplit
   949  func callee(x int) {
   950  }
   951  
   952  func main() {
   953  	callee(9)
   954  }
   955  `
   956  
   957  func TestIssue42396(t *testing.T) {
   958  	testenv.MustHaveGoBuild(t)
   959  
   960  	if !sys.RaceDetectorSupported(runtime.GOOS, runtime.GOARCH) {
   961  		t.Skip("no race detector support")
   962  	}
   963  
   964  	t.Parallel()
   965  
   966  	tmpdir := t.TempDir()
   967  
   968  	src := filepath.Join(tmpdir, "main.go")
   969  	err := ioutil.WriteFile(src, []byte(testIssue42396src), 0666)
   970  	if err != nil {
   971  		t.Fatalf("failed to write source file: %v", err)
   972  	}
   973  	exe := filepath.Join(tmpdir, "main.exe")
   974  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-race", "-o", exe, src)
   975  	out, err := cmd.CombinedOutput()
   976  	if err == nil {
   977  		t.Fatalf("build unexpectedly succeeded")
   978  	}
   979  
   980  	// Check to make sure that we see a reasonable error message
   981  	// and not a panic.
   982  	if strings.Contains(string(out), "panic:") {
   983  		t.Fatalf("build should not fail with panic:\n%s", out)
   984  	}
   985  	const want = "reference to undefined builtin"
   986  	if !strings.Contains(string(out), want) {
   987  		t.Fatalf("error message incorrect: expected it to contain %q but instead got:\n%s\n", want, out)
   988  	}
   989  }
   990  
   991  const testLargeRelocSrc = `
   992  package main
   993  
   994  var x = [1<<25]byte{1<<23: 23, 1<<24: 24}
   995  
   996  func main() {
   997  	check(x[1<<23-1], 0)
   998  	check(x[1<<23], 23)
   999  	check(x[1<<23+1], 0)
  1000  	check(x[1<<24-1], 0)
  1001  	check(x[1<<24], 24)
  1002  	check(x[1<<24+1], 0)
  1003  }
  1004  
  1005  func check(x, y byte) {
  1006  	if x != y {
  1007  		panic("FAIL")
  1008  	}
  1009  }
  1010  `
  1011  
  1012  func TestLargeReloc(t *testing.T) {
  1013  	// Test that large relocation addend is handled correctly.
  1014  	// In particular, on darwin/arm64 when external linking,
  1015  	// Mach-O relocation has only 24-bit addend. See issue #42738.
  1016  	testenv.MustHaveGoBuild(t)
  1017  	t.Parallel()
  1018  
  1019  	tmpdir := t.TempDir()
  1020  
  1021  	src := filepath.Join(tmpdir, "x.go")
  1022  	err := ioutil.WriteFile(src, []byte(testLargeRelocSrc), 0666)
  1023  	if err != nil {
  1024  		t.Fatalf("failed to write source file: %v", err)
  1025  	}
  1026  	cmd := exec.Command(testenv.GoToolPath(t), "run", src)
  1027  	out, err := cmd.CombinedOutput()
  1028  	if err != nil {
  1029  		t.Errorf("build failed: %v. output:\n%s", err, out)
  1030  	}
  1031  
  1032  	if testenv.HasCGO() { // currently all targets that support cgo can external link
  1033  		cmd = exec.Command(testenv.GoToolPath(t), "run", "-ldflags=-linkmode=external", src)
  1034  		out, err = cmd.CombinedOutput()
  1035  		if err != nil {
  1036  			t.Fatalf("build failed: %v. output:\n%s", err, out)
  1037  		}
  1038  	}
  1039  }
  1040  

View as plain text