Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/link/link_test.go

Documentation: cmd/link

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"debug/macho"
     7  	"internal/testenv"
     8  	"io/ioutil"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"regexp"
    13  	"runtime"
    14  	"strings"
    15  	"testing"
    16  )
    17  
    18  var AuthorPaidByTheColumnInch struct {
    19  	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."`
    20  
    21  	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."`
    22  
    23  	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."`
    24  
    25  	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."`
    26  }
    27  
    28  func TestLargeSymName(t *testing.T) {
    29  	// The compiler generates a symbol name using the string form of the
    30  	// type. This tests that the linker can read symbol names larger than
    31  	// the bufio buffer. Issue #15104.
    32  	_ = AuthorPaidByTheColumnInch
    33  }
    34  
    35  func TestIssue21703(t *testing.T) {
    36  	t.Parallel()
    37  
    38  	testenv.MustHaveGoBuild(t)
    39  
    40  	const source = `
    41  package main
    42  const X = "\n!\n"
    43  func main() {}
    44  `
    45  
    46  	tmpdir, err := ioutil.TempDir("", "issue21703")
    47  	if err != nil {
    48  		t.Fatalf("failed to create temp dir: %v\n", err)
    49  	}
    50  	defer os.RemoveAll(tmpdir)
    51  
    52  	err = ioutil.WriteFile(filepath.Join(tmpdir, "main.go"), []byte(source), 0666)
    53  	if err != nil {
    54  		t.Fatalf("failed to write main.go: %v\n", err)
    55  	}
    56  
    57  	cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "main.go")
    58  	cmd.Dir = tmpdir
    59  	out, err := cmd.CombinedOutput()
    60  	if err != nil {
    61  		t.Fatalf("failed to compile main.go: %v, output: %s\n", err, out)
    62  	}
    63  
    64  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "link", "main.o")
    65  	cmd.Dir = tmpdir
    66  	out, err = cmd.CombinedOutput()
    67  	if err != nil {
    68  		t.Fatalf("failed to link main.o: %v, output: %s\n", err, out)
    69  	}
    70  }
    71  
    72  // TestIssue28429 ensures that the linker does not attempt to link
    73  // sections not named *.o. Such sections may be used by a build system
    74  // to, for example, save facts produced by a modular static analysis
    75  // such as golang.org/x/tools/go/analysis.
    76  func TestIssue28429(t *testing.T) {
    77  	t.Parallel()
    78  
    79  	testenv.MustHaveGoBuild(t)
    80  
    81  	tmpdir, err := ioutil.TempDir("", "issue28429-")
    82  	if err != nil {
    83  		t.Fatalf("failed to create temp dir: %v", err)
    84  	}
    85  	defer os.RemoveAll(tmpdir)
    86  
    87  	write := func(name, content string) {
    88  		err := ioutil.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
    89  		if err != nil {
    90  			t.Fatal(err)
    91  		}
    92  	}
    93  
    94  	runGo := func(args ...string) {
    95  		cmd := exec.Command(testenv.GoToolPath(t), args...)
    96  		cmd.Dir = tmpdir
    97  		out, err := cmd.CombinedOutput()
    98  		if err != nil {
    99  			t.Fatalf("'go %s' failed: %v, output: %s",
   100  				strings.Join(args, " "), err, out)
   101  		}
   102  	}
   103  
   104  	// Compile a main package.
   105  	write("main.go", "package main; func main() {}")
   106  	runGo("tool", "compile", "-p", "main", "main.go")
   107  	runGo("tool", "pack", "c", "main.a", "main.o")
   108  
   109  	// Add an extra section with a short, non-.o name.
   110  	// This simulates an alternative build system.
   111  	write(".facts", "this is not an object file")
   112  	runGo("tool", "pack", "r", "main.a", ".facts")
   113  
   114  	// Verify that the linker does not attempt
   115  	// to compile the extra section.
   116  	runGo("tool", "link", "main.a")
   117  }
   118  
   119  func TestUnresolved(t *testing.T) {
   120  	testenv.MustHaveGoBuild(t)
   121  
   122  	tmpdir, err := ioutil.TempDir("", "unresolved-")
   123  	if err != nil {
   124  		t.Fatalf("failed to create temp dir: %v", err)
   125  	}
   126  	defer os.RemoveAll(tmpdir)
   127  
   128  	write := func(name, content string) {
   129  		err := ioutil.WriteFile(filepath.Join(tmpdir, name), []byte(content), 0666)
   130  		if err != nil {
   131  			t.Fatal(err)
   132  		}
   133  	}
   134  
   135  	// Test various undefined references. Because of issue #29852,
   136  	// this used to give confusing error messages because the
   137  	// linker would find an undefined reference to "zero" created
   138  	// by the runtime package.
   139  
   140  	write("go.mod", "module testunresolved\n")
   141  	write("main.go", `package main
   142  
   143  func main() {
   144          x()
   145  }
   146  
   147  func x()
   148  `)
   149  	write("main.s", `
   150  TEXT ·x(SB),0,$0
   151          MOVD zero<>(SB), AX
   152          MOVD zero(SB), AX
   153          MOVD ·zero(SB), AX
   154          RET
   155  `)
   156  	cmd := exec.Command(testenv.GoToolPath(t), "build")
   157  	cmd.Dir = tmpdir
   158  	cmd.Env = append(os.Environ(),
   159  		"GOARCH=amd64", "GOOS=linux", "GOPATH="+filepath.Join(tmpdir, "_gopath"))
   160  	out, err := cmd.CombinedOutput()
   161  	if err == nil {
   162  		t.Fatalf("expected build to fail, but it succeeded")
   163  	}
   164  	out = regexp.MustCompile("(?m)^#.*\n").ReplaceAll(out, nil)
   165  	got := string(out)
   166  	want := `main.x: relocation target zero not defined
   167  main.x: relocation target zero not defined
   168  main.x: relocation target main.zero not defined
   169  `
   170  	if want != got {
   171  		t.Fatalf("want:\n%sgot:\n%s", want, got)
   172  	}
   173  }
   174  
   175  func TestBuildForTvOS(t *testing.T) {
   176  	testenv.MustHaveCGO(t)
   177  	testenv.MustHaveGoBuild(t)
   178  
   179  	// Only run this on darwin/amd64, where we can cross build for tvOS.
   180  	if runtime.GOARCH != "amd64" || runtime.GOOS != "darwin" {
   181  		t.Skip("skipping on non-darwin/amd64 platform")
   182  	}
   183  	if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
   184  		t.Skip("skipping in -short mode with $GO_BUILDER_NAME empty")
   185  	}
   186  	if err := exec.Command("xcrun", "--help").Run(); err != nil {
   187  		t.Skipf("error running xcrun, required for iOS cross build: %v", err)
   188  	}
   189  
   190  	sdkPath, err := exec.Command("xcrun", "--sdk", "appletvos", "--show-sdk-path").Output()
   191  	if err != nil {
   192  		t.Skip("failed to locate appletvos SDK, skipping")
   193  	}
   194  	CC := []string{
   195  		"clang",
   196  		"-arch",
   197  		"arm64",
   198  		"-isysroot", strings.TrimSpace(string(sdkPath)),
   199  		"-mtvos-version-min=12.0",
   200  		"-fembed-bitcode",
   201  		"-framework", "CoreFoundation",
   202  	}
   203  	lib := filepath.Join("testdata", "lib.go")
   204  	tmpDir, err := ioutil.TempDir("", "go-link-TestBuildFortvOS")
   205  	if err != nil {
   206  		t.Fatal(err)
   207  	}
   208  	defer os.RemoveAll(tmpDir)
   209  
   210  	ar := filepath.Join(tmpDir, "lib.a")
   211  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", ar, lib)
   212  	cmd.Env = append(os.Environ(),
   213  		"CGO_ENABLED=1",
   214  		"GOOS=darwin",
   215  		"GOARCH=arm64",
   216  		"CC="+strings.Join(CC, " "),
   217  		"CGO_CFLAGS=", // ensure CGO_CFLAGS does not contain any flags. Issue #35459
   218  	)
   219  	if out, err := cmd.CombinedOutput(); err != nil {
   220  		t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
   221  	}
   222  
   223  	link := exec.Command(CC[0], CC[1:]...)
   224  	link.Args = append(link.Args, ar, filepath.Join("testdata", "main.m"))
   225  	if out, err := link.CombinedOutput(); err != nil {
   226  		t.Fatalf("%v: %v:\n%s", link.Args, err, out)
   227  	}
   228  }
   229  
   230  var testXFlagSrc = `
   231  package main
   232  var X = "hello"
   233  var Z = [99999]int{99998:12345} // make it large enough to be mmaped
   234  func main() { println(X) }
   235  `
   236  
   237  func TestXFlag(t *testing.T) {
   238  	testenv.MustHaveGoBuild(t)
   239  
   240  	tmpdir, err := ioutil.TempDir("", "TestXFlag")
   241  	if err != nil {
   242  		t.Fatal(err)
   243  	}
   244  	defer os.RemoveAll(tmpdir)
   245  
   246  	src := filepath.Join(tmpdir, "main.go")
   247  	err = ioutil.WriteFile(src, []byte(testXFlagSrc), 0666)
   248  	if err != nil {
   249  		t.Fatal(err)
   250  	}
   251  
   252  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-X=main.X=meow", "-o", filepath.Join(tmpdir, "main"), src)
   253  	if out, err := cmd.CombinedOutput(); err != nil {
   254  		t.Errorf("%v: %v:\n%s", cmd.Args, err, out)
   255  	}
   256  }
   257  
   258  var testMacOSVersionSrc = `
   259  package main
   260  func main() { }
   261  `
   262  
   263  func TestMacOSVersion(t *testing.T) {
   264  	testenv.MustHaveGoBuild(t)
   265  
   266  	tmpdir, err := ioutil.TempDir("", "TestMacOSVersion")
   267  	if err != nil {
   268  		t.Fatal(err)
   269  	}
   270  	defer os.RemoveAll(tmpdir)
   271  
   272  	src := filepath.Join(tmpdir, "main.go")
   273  	err = ioutil.WriteFile(src, []byte(testMacOSVersionSrc), 0666)
   274  	if err != nil {
   275  		t.Fatal(err)
   276  	}
   277  
   278  	exe := filepath.Join(tmpdir, "main")
   279  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-linkmode=internal", "-o", exe, src)
   280  	cmd.Env = append(os.Environ(),
   281  		"CGO_ENABLED=0",
   282  		"GOOS=darwin",
   283  		"GOARCH=amd64",
   284  	)
   285  	if out, err := cmd.CombinedOutput(); err != nil {
   286  		t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
   287  	}
   288  	exef, err := os.Open(exe)
   289  	if err != nil {
   290  		t.Fatal(err)
   291  	}
   292  	exem, err := macho.NewFile(exef)
   293  	if err != nil {
   294  		t.Fatal(err)
   295  	}
   296  	found := false
   297  	const LC_VERSION_MIN_MACOSX = 0x24
   298  	checkMin := func(ver uint32) {
   299  		major, minor := (ver>>16)&0xff, (ver>>8)&0xff
   300  		if major != 10 || minor < 9 {
   301  			t.Errorf("LC_VERSION_MIN_MACOSX version %d.%d < 10.9", major, minor)
   302  		}
   303  	}
   304  	for _, cmd := range exem.Loads {
   305  		raw := cmd.Raw()
   306  		type_ := exem.ByteOrder.Uint32(raw)
   307  		if type_ != LC_VERSION_MIN_MACOSX {
   308  			continue
   309  		}
   310  		osVer := exem.ByteOrder.Uint32(raw[8:])
   311  		checkMin(osVer)
   312  		sdkVer := exem.ByteOrder.Uint32(raw[12:])
   313  		checkMin(sdkVer)
   314  		found = true
   315  		break
   316  	}
   317  	if !found {
   318  		t.Errorf("no LC_VERSION_MIN_MACOSX load command found")
   319  	}
   320  }
   321  
   322  const Issue34788src = `
   323  
   324  package blah
   325  
   326  func Blah(i int) int {
   327  	a := [...]int{1, 2, 3, 4, 5, 6, 7, 8}
   328  	return a[i&7]
   329  }
   330  `
   331  
   332  func TestIssue34788Android386TLSSequence(t *testing.T) {
   333  	testenv.MustHaveGoBuild(t)
   334  
   335  	// This is a cross-compilation test, so it doesn't make
   336  	// sense to run it on every GOOS/GOARCH combination. Limit
   337  	// the test to amd64 + darwin/linux.
   338  	if runtime.GOARCH != "amd64" ||
   339  		(runtime.GOOS != "darwin" && runtime.GOOS != "linux") {
   340  		t.Skip("skipping on non-{linux,darwin}/amd64 platform")
   341  	}
   342  
   343  	tmpdir, err := ioutil.TempDir("", "TestIssue34788Android386TLSSequence")
   344  	if err != nil {
   345  		t.Fatal(err)
   346  	}
   347  	defer os.RemoveAll(tmpdir)
   348  
   349  	src := filepath.Join(tmpdir, "blah.go")
   350  	err = ioutil.WriteFile(src, []byte(Issue34788src), 0666)
   351  	if err != nil {
   352  		t.Fatal(err)
   353  	}
   354  
   355  	obj := filepath.Join(tmpdir, "blah.o")
   356  	cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", obj, src)
   357  	cmd.Env = append(os.Environ(), "GOARCH=386", "GOOS=android")
   358  	if out, err := cmd.CombinedOutput(); err != nil {
   359  		if err != nil {
   360  			t.Fatalf("failed to compile blah.go: %v, output: %s\n", err, out)
   361  		}
   362  	}
   363  
   364  	// Run objdump on the resulting object.
   365  	cmd = exec.Command(testenv.GoToolPath(t), "tool", "objdump", obj)
   366  	out, oerr := cmd.CombinedOutput()
   367  	if oerr != nil {
   368  		t.Fatalf("failed to objdump blah.o: %v, output: %s\n", oerr, out)
   369  	}
   370  
   371  	// Sift through the output; we should not be seeing any R_TLS_LE relocs.
   372  	scanner := bufio.NewScanner(bytes.NewReader(out))
   373  	for scanner.Scan() {
   374  		line := scanner.Text()
   375  		if strings.Contains(line, "R_TLS_LE") {
   376  			t.Errorf("objdump output contains unexpected R_TLS_LE reloc: %s", line)
   377  		}
   378  	}
   379  }
   380  
   381  const testStrictDupGoSrc = `
   382  package main
   383  func f()
   384  func main() { f() }
   385  `
   386  
   387  const testStrictDupAsmSrc1 = `
   388  #include "textflag.h"
   389  TEXT	·f(SB), NOSPLIT|DUPOK, $0-0
   390  	RET
   391  `
   392  
   393  const testStrictDupAsmSrc2 = `
   394  #include "textflag.h"
   395  TEXT	·f(SB), NOSPLIT|DUPOK, $0-0
   396  	JMP	0(PC)
   397  `
   398  
   399  func TestStrictDup(t *testing.T) {
   400  	// Check that -strictdups flag works.
   401  	testenv.MustHaveGoBuild(t)
   402  
   403  	tmpdir, err := ioutil.TempDir("", "TestStrictDup")
   404  	if err != nil {
   405  		t.Fatal(err)
   406  	}
   407  	defer os.RemoveAll(tmpdir)
   408  
   409  	src := filepath.Join(tmpdir, "x.go")
   410  	err = ioutil.WriteFile(src, []byte(testStrictDupGoSrc), 0666)
   411  	if err != nil {
   412  		t.Fatal(err)
   413  	}
   414  	src = filepath.Join(tmpdir, "a.s")
   415  	err = ioutil.WriteFile(src, []byte(testStrictDupAsmSrc1), 0666)
   416  	if err != nil {
   417  		t.Fatal(err)
   418  	}
   419  	src = filepath.Join(tmpdir, "b.s")
   420  	err = ioutil.WriteFile(src, []byte(testStrictDupAsmSrc2), 0666)
   421  	if err != nil {
   422  		t.Fatal(err)
   423  	}
   424  	src = filepath.Join(tmpdir, "go.mod")
   425  	err = ioutil.WriteFile(src, []byte("module teststrictdup\n"), 0666)
   426  	if err != nil {
   427  		t.Fatal(err)
   428  	}
   429  
   430  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=1")
   431  	cmd.Dir = tmpdir
   432  	out, err := cmd.CombinedOutput()
   433  	if err != nil {
   434  		t.Errorf("linking with -strictdups=1 failed: %v", err)
   435  	}
   436  	if !bytes.Contains(out, []byte("mismatched payload")) {
   437  		t.Errorf("unexpected output:\n%s", out)
   438  	}
   439  
   440  	cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-strictdups=2")
   441  	cmd.Dir = tmpdir
   442  	out, err = cmd.CombinedOutput()
   443  	if err == nil {
   444  		t.Errorf("linking with -strictdups=2 did not fail")
   445  	}
   446  	if !bytes.Contains(out, []byte("mismatched payload")) {
   447  		t.Errorf("unexpected output:\n%s", out)
   448  	}
   449  }
   450  
   451  const testTrampSrc = `
   452  package main
   453  import "fmt"
   454  func main() {
   455  	fmt.Println("hello")
   456  
   457  	defer func(){
   458  		if e := recover(); e == nil {
   459  			panic("did not panic")
   460  		}
   461  	}()
   462  	f1()
   463  }
   464  
   465  // Test deferreturn trampolines. See issue #39049.
   466  func f1() { defer f2() }
   467  func f2() { panic("XXX") }
   468  `
   469  
   470  func TestTrampoline(t *testing.T) {
   471  	// Test that trampoline insertion works as expected.
   472  	// For stress test, we set -debugtramp=2 flag, which sets a very low
   473  	// threshold for trampoline generation, and essentially all cross-package
   474  	// calls will use trampolines.
   475  	switch runtime.GOARCH {
   476  	case "arm", "ppc64", "ppc64le":
   477  	default:
   478  		t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
   479  	}
   480  	if runtime.GOOS == "aix" {
   481  		t.Skip("trampolines on AIX doesn't work in Go 1.14") // fixed in Go 1.15
   482  	}
   483  
   484  	testenv.MustHaveGoBuild(t)
   485  
   486  	tmpdir, err := ioutil.TempDir("", "TestTrampoline")
   487  	if err != nil {
   488  		t.Fatal(err)
   489  	}
   490  	defer os.RemoveAll(tmpdir)
   491  
   492  	src := filepath.Join(tmpdir, "hello.go")
   493  	err = ioutil.WriteFile(src, []byte(testTrampSrc), 0666)
   494  	if err != nil {
   495  		t.Fatal(err)
   496  	}
   497  	exe := filepath.Join(tmpdir, "hello.exe")
   498  
   499  	cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
   500  	out, err := cmd.CombinedOutput()
   501  	if err != nil {
   502  		t.Fatalf("build failed: %v\n%s", err, out)
   503  	}
   504  	cmd = exec.Command(exe)
   505  	out, err = cmd.CombinedOutput()
   506  	if err != nil {
   507  		t.Errorf("executable failed to run: %v\n%s", err, out)
   508  	}
   509  	if string(out) != "hello\n" {
   510  		t.Errorf("unexpected output:\n%s", out)
   511  	}
   512  }
   513  

View as plain text