Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/link/internal/ld/ld_test.go

Documentation: cmd/link/internal/ld

     1  // Copyright 2018 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 ld
     6  
     7  import (
     8  	"debug/pe"
     9  	"fmt"
    10  	"internal/testenv"
    11  	"io/ioutil"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"runtime"
    15  	"strings"
    16  	"testing"
    17  )
    18  
    19  func TestUndefinedRelocErrors(t *testing.T) {
    20  	testenv.MustHaveGoBuild(t)
    21  
    22  	// When external linking, symbols may be defined externally, so we allow
    23  	// undefined symbols and let external linker resolve. Skip the test.
    24  	testenv.MustInternalLink(t)
    25  
    26  	t.Parallel()
    27  
    28  	out, err := exec.Command(testenv.GoToolPath(t), "build", "./testdata/issue10978").CombinedOutput()
    29  	if err == nil {
    30  		t.Fatal("expected build to fail")
    31  	}
    32  
    33  	wantErrors := map[string]int{
    34  		// Main function has dedicated error message.
    35  		"function main is undeclared in the main package": 1,
    36  
    37  		// Single error reporting per each symbol.
    38  		// This way, duplicated messages are not reported for
    39  		// multiple relocations with a same name.
    40  		"main.defined1: relocation target main.undefined not defined": 1,
    41  		"main.defined2: relocation target main.undefined not defined": 1,
    42  	}
    43  	unexpectedErrors := map[string]int{}
    44  
    45  	for _, l := range strings.Split(string(out), "\n") {
    46  		if strings.HasPrefix(l, "#") || l == "" {
    47  			continue
    48  		}
    49  		matched := ""
    50  		for want := range wantErrors {
    51  			if strings.Contains(l, want) {
    52  				matched = want
    53  				break
    54  			}
    55  		}
    56  		if matched != "" {
    57  			wantErrors[matched]--
    58  		} else {
    59  			unexpectedErrors[l]++
    60  		}
    61  	}
    62  
    63  	for want, n := range wantErrors {
    64  		switch {
    65  		case n > 0:
    66  			t.Errorf("unmatched error: %s (x%d)", want, n)
    67  		case n < 0:
    68  			t.Errorf("extra errors: %s (x%d)", want, -n)
    69  		}
    70  	}
    71  	for unexpected, n := range unexpectedErrors {
    72  		t.Errorf("unexpected error: %s (x%d)", unexpected, n)
    73  	}
    74  }
    75  
    76  const carchiveSrcText = `
    77  package main
    78  
    79  //export GoFunc
    80  func GoFunc() {
    81  	println(42)
    82  }
    83  
    84  func main() {
    85  }
    86  `
    87  
    88  func TestArchiveBuildInvokeWithExec(t *testing.T) {
    89  	t.Parallel()
    90  	testenv.MustHaveGoBuild(t)
    91  	testenv.MustHaveCGO(t)
    92  
    93  	// run this test on just a small set of platforms (no need to test it
    94  	// across the board given the nature of the test).
    95  	pair := runtime.GOOS + "-" + runtime.GOARCH
    96  	switch pair {
    97  	case "darwin-amd64", "darwin-arm64", "linux-amd64", "freebsd-amd64":
    98  	default:
    99  		t.Skip("no need for test on " + pair)
   100  	}
   101  	switch runtime.GOOS {
   102  	case "openbsd", "windows":
   103  		t.Skip("c-archive unsupported")
   104  	}
   105  	dir := t.TempDir()
   106  
   107  	srcfile := filepath.Join(dir, "test.go")
   108  	arfile := filepath.Join(dir, "test.a")
   109  	if err := ioutil.WriteFile(srcfile, []byte(carchiveSrcText), 0666); err != nil {
   110  		t.Fatal(err)
   111  	}
   112  
   113  	ldf := fmt.Sprintf("-ldflags=-v -tmpdir=%s", dir)
   114  	argv := []string{"build", "-buildmode=c-archive", "-o", arfile, ldf, srcfile}
   115  	out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput()
   116  	if err != nil {
   117  		t.Fatalf("build failure: %s\n%s\n", err, string(out))
   118  	}
   119  
   120  	found := false
   121  	const want = "invoking archiver with syscall.Exec"
   122  	for _, l := range strings.Split(string(out), "\n") {
   123  		if strings.HasPrefix(l, want) {
   124  			found = true
   125  			break
   126  		}
   127  	}
   128  
   129  	if !found {
   130  		t.Errorf("expected '%s' in -v output, got:\n%s\n", want, string(out))
   131  	}
   132  }
   133  
   134  func TestLargeTextSectionSplitting(t *testing.T) {
   135  	switch runtime.GOARCH {
   136  	case "ppc64", "ppc64le":
   137  	case "arm64":
   138  		if runtime.GOOS == "darwin" {
   139  			break
   140  		}
   141  		fallthrough
   142  	default:
   143  		t.Skipf("text section splitting is not done in %s/%s", runtime.GOOS, runtime.GOARCH)
   144  	}
   145  
   146  	testenv.MustHaveGoBuild(t)
   147  	testenv.MustHaveCGO(t)
   148  	t.Parallel()
   149  	dir := t.TempDir()
   150  
   151  	// NB: the use of -ldflags=-debugtextsize=1048576 tells the linker to
   152  	// split text sections at a size threshold of 1M instead of the
   153  	// architected limit of 67M or larger. The choice of building cmd/go
   154  	// is arbitrary; we just need something sufficiently large that uses
   155  	// external linking.
   156  	exe := filepath.Join(dir, "go.exe")
   157  	out, eerr := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, "-ldflags=-linkmode=external -debugtextsize=1048576", "cmd/go").CombinedOutput()
   158  	if eerr != nil {
   159  		t.Fatalf("build failure: %s\n%s\n", eerr, string(out))
   160  	}
   161  
   162  	// Result should be runnable.
   163  	_, err := exec.Command(exe, "version").CombinedOutput()
   164  	if err != nil {
   165  		t.Fatal(err)
   166  	}
   167  }
   168  
   169  func TestWindowsBuildmodeCSharedASLR(t *testing.T) {
   170  	platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
   171  	switch platform {
   172  	case "windows/amd64", "windows/386":
   173  	default:
   174  		t.Skip("skipping windows amd64/386 only test")
   175  	}
   176  
   177  	testenv.MustHaveCGO(t)
   178  
   179  	t.Run("aslr", func(t *testing.T) {
   180  		testWindowsBuildmodeCSharedASLR(t, true)
   181  	})
   182  	t.Run("no-aslr", func(t *testing.T) {
   183  		testWindowsBuildmodeCSharedASLR(t, false)
   184  	})
   185  }
   186  
   187  func testWindowsBuildmodeCSharedASLR(t *testing.T, useASLR bool) {
   188  	t.Parallel()
   189  	testenv.MustHaveGoBuild(t)
   190  
   191  	dir := t.TempDir()
   192  
   193  	srcfile := filepath.Join(dir, "test.go")
   194  	objfile := filepath.Join(dir, "test.dll")
   195  	if err := ioutil.WriteFile(srcfile, []byte(`package main; func main() { print("hello") }`), 0666); err != nil {
   196  		t.Fatal(err)
   197  	}
   198  	argv := []string{"build", "-buildmode=c-shared"}
   199  	if !useASLR {
   200  		argv = append(argv, "-ldflags", "-aslr=false")
   201  	}
   202  	argv = append(argv, "-o", objfile, srcfile)
   203  	out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput()
   204  	if err != nil {
   205  		t.Fatalf("build failure: %s\n%s\n", err, string(out))
   206  	}
   207  
   208  	f, err := pe.Open(objfile)
   209  	if err != nil {
   210  		t.Fatal(err)
   211  	}
   212  	defer f.Close()
   213  	var dc uint16
   214  	switch oh := f.OptionalHeader.(type) {
   215  	case *pe.OptionalHeader32:
   216  		dc = oh.DllCharacteristics
   217  	case *pe.OptionalHeader64:
   218  		dc = oh.DllCharacteristics
   219  		hasHEVA := (dc & pe.IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA) != 0
   220  		if useASLR && !hasHEVA {
   221  			t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag is not set")
   222  		} else if !useASLR && hasHEVA {
   223  			t.Error("IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA flag should not be set")
   224  		}
   225  	default:
   226  		t.Fatalf("unexpected optional header type of %T", f.OptionalHeader)
   227  	}
   228  	hasASLR := (dc & pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0
   229  	if useASLR && !hasASLR {
   230  		t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set")
   231  	} else if !useASLR && hasASLR {
   232  		t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag should not be set")
   233  	}
   234  }
   235  
   236  // TestMemProfileCheck tests that cmd/link sets
   237  // runtime.disableMemoryProfiling if the runtime.MemProfile
   238  // symbol is unreachable after deadcode (and not dynlinking).
   239  // The runtime then uses that to set the default value of
   240  // runtime.MemProfileRate, which this test checks.
   241  func TestMemProfileCheck(t *testing.T) {
   242  	testenv.MustHaveGoBuild(t)
   243  	t.Parallel()
   244  
   245  	tests := []struct {
   246  		name    string
   247  		prog    string
   248  		wantOut string
   249  	}{
   250  		{
   251  			"no_memprofile",
   252  			`
   253  package main
   254  import "runtime"
   255  func main() {
   256  	println(runtime.MemProfileRate)
   257  }
   258  `,
   259  			"0",
   260  		},
   261  		{
   262  			"with_memprofile",
   263  			`
   264  package main
   265  import "runtime"
   266  func main() {
   267  	runtime.MemProfile(nil, false)
   268  	println(runtime.MemProfileRate)
   269  }
   270  `,
   271  			"524288",
   272  		},
   273  		{
   274  			"with_memprofile_indirect",
   275  			`
   276  package main
   277  import "runtime"
   278  var f = runtime.MemProfile
   279  func main() {
   280  	if f == nil {
   281  		panic("no f")
   282  	}
   283  	println(runtime.MemProfileRate)
   284  }
   285  `,
   286  			"524288",
   287  		},
   288  		{
   289  			"with_memprofile_runtime_pprof",
   290  			`
   291  package main
   292  import "runtime"
   293  import "runtime/pprof"
   294  func main() {
   295          _ = pprof.Profiles()
   296  	println(runtime.MemProfileRate)
   297  }
   298  `,
   299  			"524288",
   300  		},
   301  		{
   302  			"with_memprofile_http_pprof",
   303  			`
   304  package main
   305  import "runtime"
   306  import _ "net/http/pprof"
   307  func main() {
   308  	println(runtime.MemProfileRate)
   309  }
   310  `,
   311  			"524288",
   312  		},
   313  	}
   314  	for _, tt := range tests {
   315  		tt := tt
   316  		t.Run(tt.name, func(t *testing.T) {
   317  			t.Parallel()
   318  			tempDir := t.TempDir()
   319  			src := filepath.Join(tempDir, "x.go")
   320  			if err := ioutil.WriteFile(src, []byte(tt.prog), 0644); err != nil {
   321  				t.Fatal(err)
   322  			}
   323  			cmd := exec.Command(testenv.GoToolPath(t), "run", src)
   324  			out, err := cmd.CombinedOutput()
   325  			if err != nil {
   326  				t.Fatal(err)
   327  			}
   328  			got := strings.TrimSpace(string(out))
   329  			if got != tt.wantOut {
   330  				t.Errorf("got %q; want %q", got, tt.wantOut)
   331  			}
   332  		})
   333  	}
   334  }
   335  

View as plain text