github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/misc/cgo/testcarchive/carchive_test.go (about)

     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 carchive_test
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"debug/elf"
    11  	"fmt"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"strings"
    17  	"syscall"
    18  	"testing"
    19  	"time"
    20  	"unicode"
    21  )
    22  
    23  // Program to run.
    24  var bin []string
    25  
    26  // C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
    27  var cc []string
    28  
    29  // An environment with GOPATH=$(pwd).
    30  var gopathEnv []string
    31  
    32  // ".exe" on Windows.
    33  var exeSuffix string
    34  
    35  var GOOS, GOARCH string
    36  var libgodir string
    37  
    38  func init() {
    39  	GOOS = goEnv("GOOS")
    40  	GOARCH = goEnv("GOARCH")
    41  	bin = cmdToRun("./testp")
    42  
    43  	ccOut := goEnv("CC")
    44  	cc = []string{string(ccOut)}
    45  
    46  	out := goEnv("GOGCCFLAGS")
    47  	quote := '\000'
    48  	start := 0
    49  	lastSpace := true
    50  	backslash := false
    51  	s := string(out)
    52  	for i, c := range s {
    53  		if quote == '\000' && unicode.IsSpace(c) {
    54  			if !lastSpace {
    55  				cc = append(cc, s[start:i])
    56  				lastSpace = true
    57  			}
    58  		} else {
    59  			if lastSpace {
    60  				start = i
    61  				lastSpace = false
    62  			}
    63  			if quote == '\000' && !backslash && (c == '"' || c == '\'') {
    64  				quote = c
    65  				backslash = false
    66  			} else if !backslash && quote == c {
    67  				quote = '\000'
    68  			} else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
    69  				backslash = true
    70  			} else {
    71  				backslash = false
    72  			}
    73  		}
    74  	}
    75  	if !lastSpace {
    76  		cc = append(cc, s[start:])
    77  	}
    78  
    79  	if GOOS == "darwin" {
    80  		// For Darwin/ARM.
    81  		// TODO(crawshaw): can we do better?
    82  		cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
    83  	}
    84  	libgodir = GOOS + "_" + GOARCH
    85  	switch GOOS {
    86  	case "darwin":
    87  		if GOARCH == "arm" || GOARCH == "arm64" {
    88  			libgodir += "_shared"
    89  		}
    90  	case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
    91  		libgodir += "_shared"
    92  	}
    93  	cc = append(cc, "-I", filepath.Join("pkg", libgodir))
    94  
    95  	// Build an environment with GOPATH=$(pwd)
    96  	env := os.Environ()
    97  	var n []string
    98  	for _, e := range env {
    99  		if !strings.HasPrefix(e, "GOPATH=") {
   100  			n = append(n, e)
   101  		}
   102  	}
   103  	dir, err := os.Getwd()
   104  	if err != nil {
   105  		fmt.Fprintln(os.Stderr, err)
   106  		os.Exit(2)
   107  	}
   108  	n = append(n, "GOPATH="+dir)
   109  	gopathEnv = n
   110  
   111  	if GOOS == "windows" {
   112  		exeSuffix = ".exe"
   113  	}
   114  }
   115  
   116  func goEnv(key string) string {
   117  	out, err := exec.Command("go", "env", key).Output()
   118  	if err != nil {
   119  		fmt.Fprintf(os.Stderr, "go env %s failed:\n%s\n", key, err)
   120  		if ee, ok := err.(*exec.ExitError); ok {
   121  			fmt.Fprintf(os.Stderr, "%s", ee.Stderr)
   122  		}
   123  		os.Exit(2)
   124  	}
   125  	return strings.TrimSpace(string(out))
   126  }
   127  
   128  func cmdToRun(name string) []string {
   129  	execScript := "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec"
   130  	executor, err := exec.LookPath(execScript)
   131  	if err != nil {
   132  		return []string{name}
   133  	}
   134  	return []string{executor, name}
   135  }
   136  
   137  func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) {
   138  	t.Helper()
   139  	cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
   140  	cmd.Env = gopathEnv
   141  	t.Log(buildcmd)
   142  	if out, err := cmd.CombinedOutput(); err != nil {
   143  		t.Logf("%s", out)
   144  		t.Fatal(err)
   145  	}
   146  	defer func() {
   147  		os.Remove(libgoa)
   148  		os.Remove(libgoh)
   149  	}()
   150  
   151  	ccArgs := append(cc, "-o", exe, "main.c")
   152  	if GOOS == "windows" {
   153  		ccArgs = append(ccArgs, "main_windows.c", libgoa, "-lntdll", "-lws2_32", "-lwinmm")
   154  	} else {
   155  		ccArgs = append(ccArgs, "main_unix.c", libgoa)
   156  	}
   157  	t.Log(ccArgs)
   158  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   159  		t.Logf("%s", out)
   160  		t.Fatal(err)
   161  	}
   162  	defer os.Remove(exe)
   163  
   164  	binArgs := append(cmdToRun(exe), "arg1", "arg2")
   165  	if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil {
   166  		t.Logf("%s", out)
   167  		t.Fatal(err)
   168  	}
   169  }
   170  
   171  func TestInstall(t *testing.T) {
   172  	defer os.RemoveAll("pkg")
   173  
   174  	testInstall(t, "./testp1"+exeSuffix,
   175  		filepath.Join("pkg", libgodir, "libgo.a"),
   176  		filepath.Join("pkg", libgodir, "libgo.h"),
   177  		"go", "install", "-i", "-buildmode=c-archive", "libgo")
   178  
   179  	// Test building libgo other than installing it.
   180  	// Header files are now present.
   181  	testInstall(t, "./testp2"+exeSuffix, "libgo.a", "libgo.h",
   182  		"go", "build", "-buildmode=c-archive", filepath.Join("src", "libgo", "libgo.go"))
   183  
   184  	testInstall(t, "./testp3"+exeSuffix, "libgo.a", "libgo.h",
   185  		"go", "build", "-buildmode=c-archive", "-o", "libgo.a", "libgo")
   186  }
   187  
   188  func TestEarlySignalHandler(t *testing.T) {
   189  	switch GOOS {
   190  	case "darwin":
   191  		switch GOARCH {
   192  		case "arm", "arm64":
   193  			t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
   194  		}
   195  	case "windows":
   196  		t.Skip("skipping signal test on Windows")
   197  	}
   198  
   199  	defer func() {
   200  		os.Remove("libgo2.a")
   201  		os.Remove("libgo2.h")
   202  		os.Remove("testp")
   203  		os.RemoveAll("pkg")
   204  	}()
   205  
   206  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
   207  	cmd.Env = gopathEnv
   208  	if out, err := cmd.CombinedOutput(); err != nil {
   209  		t.Logf("%s", out)
   210  		t.Fatal(err)
   211  	}
   212  
   213  	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a")
   214  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   215  		t.Logf("%s", out)
   216  		t.Fatal(err)
   217  	}
   218  
   219  	if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
   220  		t.Logf("%s", out)
   221  		t.Fatal(err)
   222  	}
   223  }
   224  
   225  func TestSignalForwarding(t *testing.T) {
   226  	checkSignalForwardingTest(t)
   227  
   228  	defer func() {
   229  		os.Remove("libgo2.a")
   230  		os.Remove("libgo2.h")
   231  		os.Remove("testp")
   232  		os.RemoveAll("pkg")
   233  	}()
   234  
   235  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
   236  	cmd.Env = gopathEnv
   237  	if out, err := cmd.CombinedOutput(); err != nil {
   238  		t.Logf("%s", out)
   239  		t.Fatal(err)
   240  	}
   241  
   242  	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
   243  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   244  		t.Logf("%s", out)
   245  		t.Fatal(err)
   246  	}
   247  
   248  	cmd = exec.Command(bin[0], append(bin[1:], "1")...)
   249  
   250  	out, err := cmd.CombinedOutput()
   251  	t.Logf("%s", out)
   252  	expectSignal(t, err, syscall.SIGSEGV)
   253  
   254  	// Test SIGPIPE forwarding
   255  	cmd = exec.Command(bin[0], append(bin[1:], "3")...)
   256  
   257  	out, err = cmd.CombinedOutput()
   258  	t.Logf("%s", out)
   259  	expectSignal(t, err, syscall.SIGPIPE)
   260  }
   261  
   262  func TestSignalForwardingExternal(t *testing.T) {
   263  	checkSignalForwardingTest(t)
   264  
   265  	defer func() {
   266  		os.Remove("libgo2.a")
   267  		os.Remove("libgo2.h")
   268  		os.Remove("testp")
   269  		os.RemoveAll("pkg")
   270  	}()
   271  
   272  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
   273  	cmd.Env = gopathEnv
   274  	if out, err := cmd.CombinedOutput(); err != nil {
   275  		t.Logf("%s", out)
   276  		t.Fatal(err)
   277  	}
   278  
   279  	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
   280  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   281  		t.Logf("%s", out)
   282  		t.Fatal(err)
   283  	}
   284  
   285  	// We want to send the process a signal and see if it dies.
   286  	// Normally the signal goes to the C thread, the Go signal
   287  	// handler picks it up, sees that it is running in a C thread,
   288  	// and the program dies. Unfortunately, occasionally the
   289  	// signal is delivered to a Go thread, which winds up
   290  	// discarding it because it was sent by another program and
   291  	// there is no Go handler for it. To avoid this, run the
   292  	// program several times in the hopes that it will eventually
   293  	// fail.
   294  	const tries = 20
   295  	for i := 0; i < tries; i++ {
   296  		cmd = exec.Command(bin[0], append(bin[1:], "2")...)
   297  
   298  		stderr, err := cmd.StderrPipe()
   299  		if err != nil {
   300  			t.Fatal(err)
   301  		}
   302  		defer stderr.Close()
   303  
   304  		r := bufio.NewReader(stderr)
   305  
   306  		err = cmd.Start()
   307  
   308  		if err != nil {
   309  			t.Fatal(err)
   310  		}
   311  
   312  		// Wait for trigger to ensure that the process is started.
   313  		ok, err := r.ReadString('\n')
   314  
   315  		// Verify trigger.
   316  		if err != nil || ok != "OK\n" {
   317  			t.Fatalf("Did not receive OK signal")
   318  		}
   319  
   320  		// Give the program a chance to enter the sleep function.
   321  		time.Sleep(time.Millisecond)
   322  
   323  		cmd.Process.Signal(syscall.SIGSEGV)
   324  
   325  		err = cmd.Wait()
   326  
   327  		if err == nil {
   328  			continue
   329  		}
   330  
   331  		if expectSignal(t, err, syscall.SIGSEGV) {
   332  			return
   333  		}
   334  	}
   335  
   336  	t.Errorf("program succeeded unexpectedly %d times", tries)
   337  }
   338  
   339  // checkSignalForwardingTest calls t.Skip if the SignalForwarding test
   340  // doesn't work on this platform.
   341  func checkSignalForwardingTest(t *testing.T) {
   342  	switch GOOS {
   343  	case "darwin":
   344  		switch GOARCH {
   345  		case "arm", "arm64":
   346  			t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
   347  		}
   348  	case "windows":
   349  		t.Skip("skipping signal test on Windows")
   350  	}
   351  }
   352  
   353  // expectSignal checks that err, the exit status of a test program,
   354  // shows a failure due to a specific signal. Returns whether we found
   355  // the expected signal.
   356  func expectSignal(t *testing.T, err error, sig syscall.Signal) bool {
   357  	if err == nil {
   358  		t.Error("test program succeeded unexpectedly")
   359  	} else if ee, ok := err.(*exec.ExitError); !ok {
   360  		t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
   361  	} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
   362  		t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
   363  	} else if !ws.Signaled() || ws.Signal() != sig {
   364  		t.Errorf("got %v; expected signal %v", ee, sig)
   365  	} else {
   366  		return true
   367  	}
   368  	return false
   369  }
   370  
   371  func TestOsSignal(t *testing.T) {
   372  	switch GOOS {
   373  	case "windows":
   374  		t.Skip("skipping signal test on Windows")
   375  	}
   376  
   377  	defer func() {
   378  		os.Remove("libgo3.a")
   379  		os.Remove("libgo3.h")
   380  		os.Remove("testp")
   381  		os.RemoveAll("pkg")
   382  	}()
   383  
   384  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "libgo3")
   385  	cmd.Env = gopathEnv
   386  	if out, err := cmd.CombinedOutput(); err != nil {
   387  		t.Logf("%s", out)
   388  		t.Fatal(err)
   389  	}
   390  
   391  	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a")
   392  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   393  		t.Logf("%s", out)
   394  		t.Fatal(err)
   395  	}
   396  
   397  	if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
   398  		t.Logf("%s", out)
   399  		t.Fatal(err)
   400  	}
   401  }
   402  
   403  func TestSigaltstack(t *testing.T) {
   404  	switch GOOS {
   405  	case "windows":
   406  		t.Skip("skipping signal test on Windows")
   407  	}
   408  
   409  	defer func() {
   410  		os.Remove("libgo4.a")
   411  		os.Remove("libgo4.h")
   412  		os.Remove("testp")
   413  		os.RemoveAll("pkg")
   414  	}()
   415  
   416  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "libgo4")
   417  	cmd.Env = gopathEnv
   418  	if out, err := cmd.CombinedOutput(); err != nil {
   419  		t.Logf("%s", out)
   420  		t.Fatal(err)
   421  	}
   422  
   423  	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a")
   424  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   425  		t.Logf("%s", out)
   426  		t.Fatal(err)
   427  	}
   428  
   429  	if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
   430  		t.Logf("%s", out)
   431  		t.Fatal(err)
   432  	}
   433  }
   434  
   435  const testar = `#!/usr/bin/env bash
   436  while expr $1 : '[-]' >/dev/null; do
   437    shift
   438  done
   439  echo "testar" > $1
   440  echo "testar" > PWD/testar.ran
   441  `
   442  
   443  func TestExtar(t *testing.T) {
   444  	switch GOOS {
   445  	case "windows":
   446  		t.Skip("skipping signal test on Windows")
   447  	}
   448  
   449  	defer func() {
   450  		os.Remove("libgo4.a")
   451  		os.Remove("libgo4.h")
   452  		os.Remove("testar")
   453  		os.Remove("testar.ran")
   454  		os.RemoveAll("pkg")
   455  	}()
   456  
   457  	os.Remove("testar")
   458  	dir, err := os.Getwd()
   459  	if err != nil {
   460  		t.Fatal(err)
   461  	}
   462  	s := strings.Replace(testar, "PWD", dir, 1)
   463  	if err := ioutil.WriteFile("testar", []byte(s), 0777); err != nil {
   464  		t.Fatal(err)
   465  	}
   466  
   467  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "libgo4")
   468  	cmd.Env = gopathEnv
   469  	if out, err := cmd.CombinedOutput(); err != nil {
   470  		t.Logf("%s", out)
   471  		t.Fatal(err)
   472  	}
   473  
   474  	if _, err := os.Stat("testar.ran"); err != nil {
   475  		if os.IsNotExist(err) {
   476  			t.Error("testar does not exist after go build")
   477  		} else {
   478  			t.Errorf("error checking testar: %v", err)
   479  		}
   480  	}
   481  }
   482  
   483  func TestPIE(t *testing.T) {
   484  	switch GOOS {
   485  	case "windows", "darwin", "plan9":
   486  		t.Skipf("skipping PIE test on %s", GOOS)
   487  	}
   488  
   489  	defer func() {
   490  		os.Remove("testp" + exeSuffix)
   491  		os.RemoveAll("pkg")
   492  	}()
   493  
   494  	cmd := exec.Command("go", "install", "-i", "-buildmode=c-archive", "libgo")
   495  	cmd.Env = gopathEnv
   496  	if out, err := cmd.CombinedOutput(); err != nil {
   497  		t.Logf("%s", out)
   498  		t.Fatal(err)
   499  	}
   500  
   501  	ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", filepath.Join("pkg", libgodir, "libgo.a"))
   502  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   503  		t.Logf("%s", out)
   504  		t.Fatal(err)
   505  	}
   506  
   507  	binArgs := append(bin, "arg1", "arg2")
   508  	if out, err := exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput(); err != nil {
   509  		t.Logf("%s", out)
   510  		t.Fatal(err)
   511  	}
   512  
   513  	f, err := elf.Open("testp" + exeSuffix)
   514  	if err != nil {
   515  		t.Fatal("elf.Open failed: ", err)
   516  	}
   517  	defer f.Close()
   518  	if hasDynTag(t, f, elf.DT_TEXTREL) {
   519  		t.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix)
   520  	}
   521  }
   522  
   523  func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool {
   524  	ds := f.SectionByType(elf.SHT_DYNAMIC)
   525  	if ds == nil {
   526  		t.Error("no SHT_DYNAMIC section")
   527  		return false
   528  	}
   529  	d, err := ds.Data()
   530  	if err != nil {
   531  		t.Errorf("can't read SHT_DYNAMIC contents: %v", err)
   532  		return false
   533  	}
   534  	for len(d) > 0 {
   535  		var t elf.DynTag
   536  		switch f.Class {
   537  		case elf.ELFCLASS32:
   538  			t = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
   539  			d = d[8:]
   540  		case elf.ELFCLASS64:
   541  			t = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
   542  			d = d[16:]
   543  		}
   544  		if t == tag {
   545  			return true
   546  		}
   547  	}
   548  	return false
   549  }
   550  
   551  func TestSIGPROF(t *testing.T) {
   552  	switch GOOS {
   553  	case "windows", "plan9":
   554  		t.Skipf("skipping SIGPROF test on %s", GOOS)
   555  	case "darwin":
   556  		t.Skipf("skipping SIGPROF test on %s; see https://golang.org/issue/19320", GOOS)
   557  	}
   558  
   559  	t.Parallel()
   560  
   561  	defer func() {
   562  		os.Remove("testp6" + exeSuffix)
   563  		os.Remove("libgo6.a")
   564  		os.Remove("libgo6.h")
   565  	}()
   566  
   567  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "libgo6")
   568  	cmd.Env = gopathEnv
   569  	if out, err := cmd.CombinedOutput(); err != nil {
   570  		t.Logf("%s", out)
   571  		t.Fatal(err)
   572  	}
   573  
   574  	ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a")
   575  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   576  		t.Logf("%s", out)
   577  		t.Fatal(err)
   578  	}
   579  
   580  	argv := cmdToRun("./testp6")
   581  	cmd = exec.Command(argv[0], argv[1:]...)
   582  	if out, err := cmd.CombinedOutput(); err != nil {
   583  		t.Logf("%s", out)
   584  		t.Fatal(err)
   585  	}
   586  }
   587  
   588  // TestCompileWithoutShared tests that if we compile code without the
   589  // -shared option, we can put it into an archive. When we use the go
   590  // tool with -buildmode=c-archive, it passes -shared to the compiler,
   591  // so we override that. The go tool doesn't work this way, but Bazel
   592  // will likely do it in the future. And it ought to work. This test
   593  // was added because at one time it did not work on PPC GNU/Linux.
   594  func TestCompileWithoutShared(t *testing.T) {
   595  	// For simplicity, reuse the signal forwarding test.
   596  	checkSignalForwardingTest(t)
   597  
   598  	defer func() {
   599  		os.Remove("libgo2.a")
   600  		os.Remove("libgo2.h")
   601  	}()
   602  
   603  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "libgo2")
   604  	cmd.Env = gopathEnv
   605  	t.Log(cmd.Args)
   606  	out, err := cmd.CombinedOutput()
   607  	t.Logf("%s", out)
   608  	if err != nil {
   609  		t.Fatal(err)
   610  	}
   611  
   612  	exe := "./testnoshared" + exeSuffix
   613  
   614  	// In some cases, -no-pie is needed here, but not accepted everywhere. First try
   615  	// if -no-pie is accepted. See #22126.
   616  	ccArgs := append(cc, "-o", exe, "-no-pie", "main5.c", "libgo2.a")
   617  	t.Log(ccArgs)
   618  	out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
   619  
   620  	// If -no-pie unrecognized, try -nopie if this is possibly clang
   621  	if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") {
   622  		ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a")
   623  		t.Log(ccArgs)
   624  		out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
   625  	}
   626  
   627  	// Don't use either -no-pie or -nopie
   628  	if err != nil && bytes.Contains(out, []byte("unrecognized")) {
   629  		ccArgs := append(cc, "-o", exe, "main5.c", "libgo2.a")
   630  		t.Log(ccArgs)
   631  		out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
   632  	}
   633  	t.Logf("%s", out)
   634  	if err != nil {
   635  		t.Fatal(err)
   636  	}
   637  	defer os.Remove(exe)
   638  
   639  	binArgs := append(cmdToRun(exe), "3")
   640  	t.Log(binArgs)
   641  	out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
   642  	t.Logf("%s", out)
   643  	expectSignal(t, err, syscall.SIGPIPE)
   644  }
   645  
   646  // Test that installing a second time recreates the header files.
   647  func TestCachedInstall(t *testing.T) {
   648  	defer os.RemoveAll("pkg")
   649  
   650  	h1 := filepath.Join("pkg", libgodir, "libgo.h")
   651  	h2 := filepath.Join("pkg", libgodir, "p.h")
   652  
   653  	buildcmd := []string{"go", "install", "-i", "-buildmode=c-archive", "libgo"}
   654  
   655  	cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
   656  	cmd.Env = gopathEnv
   657  	t.Log(buildcmd)
   658  	if out, err := cmd.CombinedOutput(); err != nil {
   659  		t.Logf("%s", out)
   660  		t.Fatal(err)
   661  	}
   662  
   663  	if _, err := os.Stat(h1); err != nil {
   664  		t.Errorf("libgo.h not installed: %v", err)
   665  	}
   666  	if _, err := os.Stat(h2); err != nil {
   667  		t.Errorf("p.h not installed: %v", err)
   668  	}
   669  
   670  	if err := os.Remove(h1); err != nil {
   671  		t.Fatal(err)
   672  	}
   673  	if err := os.Remove(h2); err != nil {
   674  		t.Fatal(err)
   675  	}
   676  
   677  	cmd = exec.Command(buildcmd[0], buildcmd[1:]...)
   678  	cmd.Env = gopathEnv
   679  	t.Log(buildcmd)
   680  	if out, err := cmd.CombinedOutput(); err != nil {
   681  		t.Logf("%s", out)
   682  		t.Fatal(err)
   683  	}
   684  
   685  	if _, err := os.Stat(h1); err != nil {
   686  		t.Errorf("libgo.h not installed in second run: %v", err)
   687  	}
   688  	if _, err := os.Stat(h2); err != nil {
   689  		t.Errorf("p.h not installed in second run: %v", err)
   690  	}
   691  }