github.com/360nenz/gumt@v0.0.0-20230623171552-8f1e25eda00d/gulang/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  	"flag"
    12  	"fmt"
    13  	"io"
    14  	"log"
    15  	"os"
    16  	"os/exec"
    17  	"path/filepath"
    18  	"regexp"
    19  	"runtime"
    20  	"strconv"
    21  	"strings"
    22  	"sync"
    23  	"syscall"
    24  	"testing"
    25  	"time"
    26  	"unicode"
    27  )
    28  
    29  // Program to run.
    30  var bin []string
    31  
    32  // C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
    33  var cc []string
    34  
    35  // ".exe" on Windows.
    36  var exeSuffix string
    37  
    38  var GOOS, GOARCH, GOPATH string
    39  var libgodir string
    40  
    41  var testWork bool // If true, preserve temporary directories.
    42  
    43  func TestMain(m *testing.M) {
    44  	flag.BoolVar(&testWork, "testwork", false, "if true, log and preserve the test's temporary working directory")
    45  	flag.Parse()
    46  	if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
    47  		fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n")
    48  		os.Exit(0)
    49  	}
    50  	if runtime.GOOS == "linux" {
    51  		if _, err := os.Stat("/etc/alpine-release"); err == nil {
    52  			fmt.Printf("SKIP - skipping failing test on alpine - go.dev/issue/19938\n")
    53  			os.Exit(0)
    54  		}
    55  	}
    56  
    57  	log.SetFlags(log.Lshortfile)
    58  	os.Exit(testMain(m))
    59  }
    60  
    61  func testMain(m *testing.M) int {
    62  	// We need a writable GOPATH in which to run the tests.
    63  	// Construct one in a temporary directory.
    64  	var err error
    65  	GOPATH, err = os.MkdirTemp("", "carchive_test")
    66  	if err != nil {
    67  		log.Panic(err)
    68  	}
    69  	if testWork {
    70  		log.Println(GOPATH)
    71  	} else {
    72  		defer os.RemoveAll(GOPATH)
    73  	}
    74  	os.Setenv("GOPATH", GOPATH)
    75  
    76  	// Copy testdata into GOPATH/src/testarchive, along with a go.mod file
    77  	// declaring the same path.
    78  	modRoot := filepath.Join(GOPATH, "src", "testcarchive")
    79  	if err := overlayDir(modRoot, "testdata"); err != nil {
    80  		log.Panic(err)
    81  	}
    82  	if err := os.Chdir(modRoot); err != nil {
    83  		log.Panic(err)
    84  	}
    85  	os.Setenv("PWD", modRoot)
    86  	if err := os.WriteFile("go.mod", []byte("module testcarchive\n"), 0666); err != nil {
    87  		log.Panic(err)
    88  	}
    89  
    90  	GOOS = goEnv("GOOS")
    91  	GOARCH = goEnv("GOARCH")
    92  	bin = cmdToRun("./testp")
    93  
    94  	ccOut := goEnv("CC")
    95  	cc = []string{string(ccOut)}
    96  
    97  	out := goEnv("GOGCCFLAGS")
    98  	quote := '\000'
    99  	start := 0
   100  	lastSpace := true
   101  	backslash := false
   102  	s := string(out)
   103  	for i, c := range s {
   104  		if quote == '\000' && unicode.IsSpace(c) {
   105  			if !lastSpace {
   106  				cc = append(cc, s[start:i])
   107  				lastSpace = true
   108  			}
   109  		} else {
   110  			if lastSpace {
   111  				start = i
   112  				lastSpace = false
   113  			}
   114  			if quote == '\000' && !backslash && (c == '"' || c == '\'') {
   115  				quote = c
   116  				backslash = false
   117  			} else if !backslash && quote == c {
   118  				quote = '\000'
   119  			} else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
   120  				backslash = true
   121  			} else {
   122  				backslash = false
   123  			}
   124  		}
   125  	}
   126  	if !lastSpace {
   127  		cc = append(cc, s[start:])
   128  	}
   129  
   130  	if GOOS == "aix" {
   131  		// -Wl,-bnoobjreorder is mandatory to keep the same layout
   132  		// in .text section.
   133  		cc = append(cc, "-Wl,-bnoobjreorder")
   134  	}
   135  	libbase := GOOS + "_" + GOARCH
   136  	if runtime.Compiler == "gccgo" {
   137  		libbase = "gccgo_" + libgodir + "_fPIC"
   138  	} else {
   139  		switch GOOS {
   140  		case "darwin", "ios":
   141  			if GOARCH == "arm64" {
   142  				libbase += "_shared"
   143  			}
   144  		case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris", "illumos":
   145  			libbase += "_shared"
   146  		}
   147  	}
   148  	libgodir = filepath.Join(GOPATH, "pkg", libbase, "testcarchive")
   149  	cc = append(cc, "-I", libgodir)
   150  
   151  	// Force reallocation (and avoid aliasing bugs) for parallel tests that append to cc.
   152  	cc = cc[:len(cc):len(cc)]
   153  
   154  	if GOOS == "windows" {
   155  		exeSuffix = ".exe"
   156  	}
   157  
   158  	return m.Run()
   159  }
   160  
   161  func goEnv(key string) string {
   162  	out, err := exec.Command("go", "env", key).Output()
   163  	if err != nil {
   164  		if ee, ok := err.(*exec.ExitError); ok {
   165  			fmt.Fprintf(os.Stderr, "%s", ee.Stderr)
   166  		}
   167  		log.Panicf("go env %s failed:\n%s\n", key, err)
   168  	}
   169  	return strings.TrimSpace(string(out))
   170  }
   171  
   172  func cmdToRun(name string) []string {
   173  	execScript := "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec"
   174  	executor, err := exec.LookPath(execScript)
   175  	if err != nil {
   176  		return []string{name}
   177  	}
   178  	return []string{executor, name}
   179  }
   180  
   181  // genHeader writes a C header file for the C-exported declarations found in .go
   182  // source files in dir.
   183  //
   184  // TODO(golang.org/issue/35715): This should be simpler.
   185  func genHeader(t *testing.T, header, dir string) {
   186  	t.Helper()
   187  
   188  	// The 'cgo' command generates a number of additional artifacts,
   189  	// but we're only interested in the header.
   190  	// Shunt the rest of the outputs to a temporary directory.
   191  	objDir, err := os.MkdirTemp(GOPATH, "_obj")
   192  	if err != nil {
   193  		t.Fatal(err)
   194  	}
   195  	defer os.RemoveAll(objDir)
   196  
   197  	files, err := filepath.Glob(filepath.Join(dir, "*.go"))
   198  	if err != nil {
   199  		t.Fatal(err)
   200  	}
   201  
   202  	cmd := exec.Command("go", "tool", "cgo",
   203  		"-objdir", objDir,
   204  		"-exportheader", header)
   205  	cmd.Args = append(cmd.Args, files...)
   206  	t.Log(cmd.Args)
   207  	if out, err := cmd.CombinedOutput(); err != nil {
   208  		t.Logf("%s", out)
   209  		t.Fatal(err)
   210  	}
   211  }
   212  
   213  func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) {
   214  	t.Helper()
   215  	cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
   216  	cmd.Env = append(cmd.Environ(), "GO111MODULE=off") // 'go install' only works in GOPATH mode
   217  	t.Log(buildcmd)
   218  	if out, err := cmd.CombinedOutput(); err != nil {
   219  		t.Logf("%s", out)
   220  		t.Fatal(err)
   221  	}
   222  	if !testWork {
   223  		defer func() {
   224  			os.Remove(libgoa)
   225  			os.Remove(libgoh)
   226  		}()
   227  	}
   228  
   229  	ccArgs := append(cc, "-o", exe, "main.c")
   230  	if GOOS == "windows" {
   231  		ccArgs = append(ccArgs, "main_windows.c", libgoa, "-lntdll", "-lws2_32", "-lwinmm")
   232  	} else {
   233  		ccArgs = append(ccArgs, "main_unix.c", libgoa)
   234  	}
   235  	if runtime.Compiler == "gccgo" {
   236  		ccArgs = append(ccArgs, "-lgo")
   237  	}
   238  	t.Log(ccArgs)
   239  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   240  		t.Logf("%s", out)
   241  		t.Fatal(err)
   242  	}
   243  	if !testWork {
   244  		defer os.Remove(exe)
   245  	}
   246  
   247  	binArgs := append(cmdToRun(exe), "arg1", "arg2")
   248  	cmd = exec.Command(binArgs[0], binArgs[1:]...)
   249  	if runtime.Compiler == "gccgo" {
   250  		cmd.Env = append(cmd.Environ(), "GCCGO=1")
   251  	}
   252  	if out, err := cmd.CombinedOutput(); err != nil {
   253  		t.Logf("%s", out)
   254  		t.Fatal(err)
   255  	}
   256  
   257  	checkLineComments(t, libgoh)
   258  }
   259  
   260  var badLineRegexp = regexp.MustCompile(`(?m)^#line [0-9]+ "/.*$`)
   261  
   262  // checkLineComments checks that the export header generated by
   263  // -buildmode=c-archive doesn't have any absolute paths in the #line
   264  // comments. We don't want those paths because they are unhelpful for
   265  // the user and make the files change based on details of the location
   266  // of GOPATH.
   267  func checkLineComments(t *testing.T, hdrname string) {
   268  	hdr, err := os.ReadFile(hdrname)
   269  	if err != nil {
   270  		if !os.IsNotExist(err) {
   271  			t.Error(err)
   272  		}
   273  		return
   274  	}
   275  	if line := badLineRegexp.Find(hdr); line != nil {
   276  		t.Errorf("bad #line directive with absolute path in %s: %q", hdrname, line)
   277  	}
   278  }
   279  
   280  // checkArchive verifies that the created library looks OK.
   281  // We just check a couple of things now, we can add more checks as needed.
   282  func checkArchive(t *testing.T, arname string) {
   283  	t.Helper()
   284  
   285  	switch GOOS {
   286  	case "aix", "darwin", "ios", "windows":
   287  		// We don't have any checks for non-ELF libraries yet.
   288  		if _, err := os.Stat(arname); err != nil {
   289  			t.Errorf("archive %s does not exist: %v", arname, err)
   290  		}
   291  	default:
   292  		checkELFArchive(t, arname)
   293  	}
   294  }
   295  
   296  // checkELFArchive checks an ELF archive.
   297  func checkELFArchive(t *testing.T, arname string) {
   298  	t.Helper()
   299  
   300  	f, err := os.Open(arname)
   301  	if err != nil {
   302  		t.Errorf("archive %s does not exist: %v", arname, err)
   303  		return
   304  	}
   305  	defer f.Close()
   306  
   307  	// TODO(iant): put these in a shared package?  But where?
   308  	const (
   309  		magic = "!<arch>\n"
   310  		fmag  = "`\n"
   311  
   312  		namelen = 16
   313  		datelen = 12
   314  		uidlen  = 6
   315  		gidlen  = 6
   316  		modelen = 8
   317  		sizelen = 10
   318  		fmaglen = 2
   319  		hdrlen  = namelen + datelen + uidlen + gidlen + modelen + sizelen + fmaglen
   320  	)
   321  
   322  	type arhdr struct {
   323  		name string
   324  		date string
   325  		uid  string
   326  		gid  string
   327  		mode string
   328  		size string
   329  		fmag string
   330  	}
   331  
   332  	var magbuf [len(magic)]byte
   333  	if _, err := io.ReadFull(f, magbuf[:]); err != nil {
   334  		t.Errorf("%s: archive too short", arname)
   335  		return
   336  	}
   337  	if string(magbuf[:]) != magic {
   338  		t.Errorf("%s: incorrect archive magic string %q", arname, magbuf)
   339  	}
   340  
   341  	off := int64(len(magic))
   342  	for {
   343  		if off&1 != 0 {
   344  			var b [1]byte
   345  			if _, err := f.Read(b[:]); err != nil {
   346  				if err == io.EOF {
   347  					break
   348  				}
   349  				t.Errorf("%s: error skipping alignment byte at %d: %v", arname, off, err)
   350  			}
   351  			off++
   352  		}
   353  
   354  		var hdrbuf [hdrlen]byte
   355  		if _, err := io.ReadFull(f, hdrbuf[:]); err != nil {
   356  			if err == io.EOF {
   357  				break
   358  			}
   359  			t.Errorf("%s: error reading archive header at %d: %v", arname, off, err)
   360  			return
   361  		}
   362  
   363  		var hdr arhdr
   364  		hdrslice := hdrbuf[:]
   365  		set := func(len int, ps *string) {
   366  			*ps = string(bytes.TrimSpace(hdrslice[:len]))
   367  			hdrslice = hdrslice[len:]
   368  		}
   369  		set(namelen, &hdr.name)
   370  		set(datelen, &hdr.date)
   371  		set(uidlen, &hdr.uid)
   372  		set(gidlen, &hdr.gid)
   373  		set(modelen, &hdr.mode)
   374  		set(sizelen, &hdr.size)
   375  		hdr.fmag = string(hdrslice[:fmaglen])
   376  		hdrslice = hdrslice[fmaglen:]
   377  		if len(hdrslice) != 0 {
   378  			t.Fatalf("internal error: len(hdrslice) == %d", len(hdrslice))
   379  		}
   380  
   381  		if hdr.fmag != fmag {
   382  			t.Errorf("%s: invalid fmagic value %q at %d", arname, hdr.fmag, off)
   383  			return
   384  		}
   385  
   386  		size, err := strconv.ParseInt(hdr.size, 10, 64)
   387  		if err != nil {
   388  			t.Errorf("%s: error parsing size %q at %d: %v", arname, hdr.size, off, err)
   389  			return
   390  		}
   391  
   392  		off += hdrlen
   393  
   394  		switch hdr.name {
   395  		case "__.SYMDEF", "/", "/SYM64/":
   396  			// The archive symbol map.
   397  		case "//", "ARFILENAMES/":
   398  			// The extended name table.
   399  		default:
   400  			// This should be an ELF object.
   401  			checkELFArchiveObject(t, arname, off, io.NewSectionReader(f, off, size))
   402  		}
   403  
   404  		off += size
   405  		if _, err := f.Seek(off, io.SeekStart); err != nil {
   406  			t.Errorf("%s: failed to seek to %d: %v", arname, off, err)
   407  		}
   408  	}
   409  }
   410  
   411  // checkELFArchiveObject checks an object in an ELF archive.
   412  func checkELFArchiveObject(t *testing.T, arname string, off int64, obj io.ReaderAt) {
   413  	t.Helper()
   414  
   415  	ef, err := elf.NewFile(obj)
   416  	if err != nil {
   417  		t.Errorf("%s: failed to open ELF file at %d: %v", arname, off, err)
   418  		return
   419  	}
   420  	defer ef.Close()
   421  
   422  	// Verify section types.
   423  	for _, sec := range ef.Sections {
   424  		want := elf.SHT_NULL
   425  		switch sec.Name {
   426  		case ".text", ".data":
   427  			want = elf.SHT_PROGBITS
   428  		case ".bss":
   429  			want = elf.SHT_NOBITS
   430  		case ".symtab":
   431  			want = elf.SHT_SYMTAB
   432  		case ".strtab":
   433  			want = elf.SHT_STRTAB
   434  		case ".init_array":
   435  			want = elf.SHT_INIT_ARRAY
   436  		case ".fini_array":
   437  			want = elf.SHT_FINI_ARRAY
   438  		case ".preinit_array":
   439  			want = elf.SHT_PREINIT_ARRAY
   440  		}
   441  		if want != elf.SHT_NULL && sec.Type != want {
   442  			t.Errorf("%s: incorrect section type in elf file at %d for section %q: got %v want %v", arname, off, sec.Name, sec.Type, want)
   443  		}
   444  	}
   445  }
   446  
   447  func TestInstall(t *testing.T) {
   448  	if !testWork {
   449  		defer os.RemoveAll(filepath.Join(GOPATH, "pkg"))
   450  	}
   451  
   452  	libgoa := "libgo.a"
   453  	if runtime.Compiler == "gccgo" {
   454  		libgoa = "liblibgo.a"
   455  	}
   456  
   457  	// Generate the p.h header file.
   458  	//
   459  	// 'go install -i -buildmode=c-archive ./libgo' would do that too, but that
   460  	// would also attempt to install transitive standard-library dependencies to
   461  	// GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may
   462  	// be running this test in a GOROOT owned by root.)
   463  	genHeader(t, "p.h", "./p")
   464  
   465  	testInstall(t, "./testp1"+exeSuffix,
   466  		filepath.Join(libgodir, libgoa),
   467  		filepath.Join(libgodir, "libgo.h"),
   468  		"go", "install", "-buildmode=c-archive", "./libgo")
   469  
   470  	// Test building libgo other than installing it.
   471  	// Header files are now present.
   472  	testInstall(t, "./testp2"+exeSuffix, "libgo.a", "libgo.h",
   473  		"go", "build", "-buildmode=c-archive", filepath.Join(".", "libgo", "libgo.go"))
   474  
   475  	testInstall(t, "./testp3"+exeSuffix, "libgo.a", "libgo.h",
   476  		"go", "build", "-buildmode=c-archive", "-o", "libgo.a", "./libgo")
   477  }
   478  
   479  func TestEarlySignalHandler(t *testing.T) {
   480  	switch GOOS {
   481  	case "darwin", "ios":
   482  		switch GOARCH {
   483  		case "arm64":
   484  			t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
   485  		}
   486  	case "windows":
   487  		t.Skip("skipping signal test on Windows")
   488  	}
   489  
   490  	if !testWork {
   491  		defer func() {
   492  			os.Remove("libgo2.a")
   493  			os.Remove("libgo2.h")
   494  			os.Remove("testp" + exeSuffix)
   495  			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
   496  		}()
   497  	}
   498  
   499  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
   500  	if out, err := cmd.CombinedOutput(); err != nil {
   501  		t.Logf("%s", out)
   502  		t.Fatal(err)
   503  	}
   504  	checkLineComments(t, "libgo2.h")
   505  	checkArchive(t, "libgo2.a")
   506  
   507  	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a")
   508  	if runtime.Compiler == "gccgo" {
   509  		ccArgs = append(ccArgs, "-lgo")
   510  	}
   511  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   512  		t.Logf("%s", out)
   513  		t.Fatal(err)
   514  	}
   515  
   516  	darwin := "0"
   517  	if runtime.GOOS == "darwin" {
   518  		darwin = "1"
   519  	}
   520  	cmd = exec.Command(bin[0], append(bin[1:], darwin)...)
   521  
   522  	if out, err := cmd.CombinedOutput(); err != nil {
   523  		t.Logf("%s", out)
   524  		t.Fatal(err)
   525  	}
   526  }
   527  
   528  func TestSignalForwarding(t *testing.T) {
   529  	checkSignalForwardingTest(t)
   530  	buildSignalForwardingTest(t)
   531  
   532  	cmd := exec.Command(bin[0], append(bin[1:], "1")...)
   533  
   534  	out, err := cmd.CombinedOutput()
   535  	t.Logf("%v\n%s", cmd.Args, out)
   536  	expectSignal(t, err, syscall.SIGSEGV, 0)
   537  
   538  	// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
   539  	if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
   540  		// Test SIGPIPE forwarding
   541  		cmd = exec.Command(bin[0], append(bin[1:], "3")...)
   542  
   543  		out, err = cmd.CombinedOutput()
   544  		if len(out) > 0 {
   545  			t.Logf("%s", out)
   546  		}
   547  		expectSignal(t, err, syscall.SIGPIPE, 0)
   548  	}
   549  }
   550  
   551  func TestSignalForwardingExternal(t *testing.T) {
   552  	if GOOS == "freebsd" || GOOS == "aix" {
   553  		t.Skipf("skipping on %s/%s; signal always goes to the Go runtime", GOOS, GOARCH)
   554  	} else if GOOS == "darwin" && GOARCH == "amd64" {
   555  		t.Skipf("skipping on %s/%s: runtime does not permit SI_USER SIGSEGV", GOOS, GOARCH)
   556  	}
   557  	checkSignalForwardingTest(t)
   558  	buildSignalForwardingTest(t)
   559  
   560  	// We want to send the process a signal and see if it dies.
   561  	// Normally the signal goes to the C thread, the Go signal
   562  	// handler picks it up, sees that it is running in a C thread,
   563  	// and the program dies. Unfortunately, occasionally the
   564  	// signal is delivered to a Go thread, which winds up
   565  	// discarding it because it was sent by another program and
   566  	// there is no Go handler for it. To avoid this, run the
   567  	// program several times in the hopes that it will eventually
   568  	// fail.
   569  	const tries = 20
   570  	for i := 0; i < tries; i++ {
   571  		err := runSignalForwardingTest(t, "2")
   572  		if err == nil {
   573  			continue
   574  		}
   575  
   576  		// If the signal is delivered to a C thread, as expected,
   577  		// the Go signal handler will disable itself and re-raise
   578  		// the signal, causing the program to die with SIGSEGV.
   579  		//
   580  		// It is also possible that the signal will be
   581  		// delivered to a Go thread, such as a GC thread.
   582  		// Currently when the Go runtime sees that a SIGSEGV was
   583  		// sent from a different program, it first tries to send
   584  		// the signal to the os/signal API. If nothing is looking
   585  		// for (or explicitly ignoring) SIGSEGV, then it crashes.
   586  		// Because the Go runtime is invoked via a c-archive,
   587  		// it treats this as GOTRACEBACK=crash, meaning that it
   588  		// dumps a stack trace for all goroutines, which it does
   589  		// by raising SIGQUIT. The effect is that we will see the
   590  		// program die with SIGQUIT in that case, not SIGSEGV.
   591  		if expectSignal(t, err, syscall.SIGSEGV, syscall.SIGQUIT) {
   592  			return
   593  		}
   594  	}
   595  
   596  	t.Errorf("program succeeded unexpectedly %d times", tries)
   597  }
   598  
   599  func TestSignalForwardingGo(t *testing.T) {
   600  	// This test fails on darwin-amd64 because of the special
   601  	// handling of user-generated SIGSEGV signals in fixsigcode in
   602  	// runtime/signal_darwin_amd64.go.
   603  	if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" {
   604  		t.Skip("not supported on darwin-amd64")
   605  	}
   606  
   607  	checkSignalForwardingTest(t)
   608  	buildSignalForwardingTest(t)
   609  	err := runSignalForwardingTest(t, "4")
   610  
   611  	// Occasionally the signal will be delivered to a C thread,
   612  	// and the program will crash with SIGSEGV.
   613  	expectSignal(t, err, syscall.SIGQUIT, syscall.SIGSEGV)
   614  }
   615  
   616  // checkSignalForwardingTest calls t.Skip if the SignalForwarding test
   617  // doesn't work on this platform.
   618  func checkSignalForwardingTest(t *testing.T) {
   619  	switch GOOS {
   620  	case "darwin", "ios":
   621  		switch GOARCH {
   622  		case "arm64":
   623  			t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
   624  		}
   625  	case "windows":
   626  		t.Skip("skipping signal test on Windows")
   627  	}
   628  }
   629  
   630  // buildSignalForwardingTest builds the executable used by the various
   631  // signal forwarding tests.
   632  func buildSignalForwardingTest(t *testing.T) {
   633  	if !testWork {
   634  		t.Cleanup(func() {
   635  			os.Remove("libgo2.a")
   636  			os.Remove("libgo2.h")
   637  			os.Remove("testp" + exeSuffix)
   638  			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
   639  		})
   640  	}
   641  
   642  	t.Log("go build -buildmode=c-archive -o libgo2.a ./libgo2")
   643  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
   644  	out, err := cmd.CombinedOutput()
   645  	if len(out) > 0 {
   646  		t.Logf("%s", out)
   647  	}
   648  	if err != nil {
   649  		t.Fatal(err)
   650  	}
   651  
   652  	checkLineComments(t, "libgo2.h")
   653  	checkArchive(t, "libgo2.a")
   654  
   655  	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
   656  	if runtime.Compiler == "gccgo" {
   657  		ccArgs = append(ccArgs, "-lgo")
   658  	}
   659  	t.Log(ccArgs)
   660  	out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
   661  	if len(out) > 0 {
   662  		t.Logf("%s", out)
   663  	}
   664  	if err != nil {
   665  		t.Fatal(err)
   666  	}
   667  }
   668  
   669  func runSignalForwardingTest(t *testing.T, arg string) error {
   670  	t.Logf("%v %s", bin, arg)
   671  	cmd := exec.Command(bin[0], append(bin[1:], arg)...)
   672  
   673  	var out strings.Builder
   674  	cmd.Stdout = &out
   675  
   676  	stderr, err := cmd.StderrPipe()
   677  	if err != nil {
   678  		t.Fatal(err)
   679  	}
   680  	defer stderr.Close()
   681  
   682  	r := bufio.NewReader(stderr)
   683  
   684  	err = cmd.Start()
   685  	if err != nil {
   686  		t.Fatal(err)
   687  	}
   688  
   689  	// Wait for trigger to ensure that process is started.
   690  	ok, err := r.ReadString('\n')
   691  
   692  	// Verify trigger.
   693  	if err != nil || ok != "OK\n" {
   694  		t.Fatal("Did not receive OK signal")
   695  	}
   696  
   697  	var wg sync.WaitGroup
   698  	wg.Add(1)
   699  	var errsb strings.Builder
   700  	go func() {
   701  		defer wg.Done()
   702  		io.Copy(&errsb, r)
   703  	}()
   704  
   705  	// Give the program a chance to enter the function.
   706  	// If the program doesn't get there the test will still
   707  	// pass, although it doesn't quite test what we intended.
   708  	// This is fine as long as the program normally makes it.
   709  	time.Sleep(time.Millisecond)
   710  
   711  	cmd.Process.Signal(syscall.SIGSEGV)
   712  
   713  	err = cmd.Wait()
   714  
   715  	s := out.String()
   716  	if len(s) > 0 {
   717  		t.Log(s)
   718  	}
   719  	wg.Wait()
   720  	s = errsb.String()
   721  	if len(s) > 0 {
   722  		t.Log(s)
   723  	}
   724  
   725  	return err
   726  }
   727  
   728  // expectSignal checks that err, the exit status of a test program,
   729  // shows a failure due to a specific signal or two. Returns whether we
   730  // found an expected signal.
   731  func expectSignal(t *testing.T, err error, sig1, sig2 syscall.Signal) bool {
   732  	t.Helper()
   733  	if err == nil {
   734  		t.Error("test program succeeded unexpectedly")
   735  	} else if ee, ok := err.(*exec.ExitError); !ok {
   736  		t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
   737  	} else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
   738  		t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
   739  	} else if !ws.Signaled() || (ws.Signal() != sig1 && ws.Signal() != sig2) {
   740  		if sig2 == 0 {
   741  			t.Errorf("got %q; expected signal %q", ee, sig1)
   742  		} else {
   743  			t.Errorf("got %q; expected signal %q or %q", ee, sig1, sig2)
   744  		}
   745  	} else {
   746  		return true
   747  	}
   748  	return false
   749  }
   750  
   751  func TestOsSignal(t *testing.T) {
   752  	switch GOOS {
   753  	case "windows":
   754  		t.Skip("skipping signal test on Windows")
   755  	}
   756  
   757  	if !testWork {
   758  		defer func() {
   759  			os.Remove("libgo3.a")
   760  			os.Remove("libgo3.h")
   761  			os.Remove("testp" + exeSuffix)
   762  			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
   763  		}()
   764  	}
   765  
   766  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "./libgo3")
   767  	if out, err := cmd.CombinedOutput(); err != nil {
   768  		t.Logf("%s", out)
   769  		t.Fatal(err)
   770  	}
   771  	checkLineComments(t, "libgo3.h")
   772  	checkArchive(t, "libgo3.a")
   773  
   774  	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a")
   775  	if runtime.Compiler == "gccgo" {
   776  		ccArgs = append(ccArgs, "-lgo")
   777  	}
   778  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   779  		t.Logf("%s", out)
   780  		t.Fatal(err)
   781  	}
   782  
   783  	if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
   784  		t.Logf("%s", out)
   785  		t.Fatal(err)
   786  	}
   787  }
   788  
   789  func TestSigaltstack(t *testing.T) {
   790  	switch GOOS {
   791  	case "windows":
   792  		t.Skip("skipping signal test on Windows")
   793  	}
   794  
   795  	if !testWork {
   796  		defer func() {
   797  			os.Remove("libgo4.a")
   798  			os.Remove("libgo4.h")
   799  			os.Remove("testp" + exeSuffix)
   800  			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
   801  		}()
   802  	}
   803  
   804  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "./libgo4")
   805  	if out, err := cmd.CombinedOutput(); err != nil {
   806  		t.Logf("%s", out)
   807  		t.Fatal(err)
   808  	}
   809  	checkLineComments(t, "libgo4.h")
   810  	checkArchive(t, "libgo4.a")
   811  
   812  	ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a")
   813  	if runtime.Compiler == "gccgo" {
   814  		ccArgs = append(ccArgs, "-lgo")
   815  	}
   816  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   817  		t.Logf("%s", out)
   818  		t.Fatal(err)
   819  	}
   820  
   821  	if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
   822  		t.Logf("%s", out)
   823  		t.Fatal(err)
   824  	}
   825  }
   826  
   827  const testar = `#!/usr/bin/env bash
   828  while [[ $1 == -* ]] >/dev/null; do
   829    shift
   830  done
   831  echo "testar" > $1
   832  echo "testar" > PWD/testar.ran
   833  `
   834  
   835  func TestExtar(t *testing.T) {
   836  	switch GOOS {
   837  	case "windows":
   838  		t.Skip("skipping signal test on Windows")
   839  	}
   840  	if runtime.Compiler == "gccgo" {
   841  		t.Skip("skipping -extar test when using gccgo")
   842  	}
   843  	if runtime.GOOS == "ios" {
   844  		t.Skip("shell scripts are not executable on iOS hosts")
   845  	}
   846  
   847  	if !testWork {
   848  		defer func() {
   849  			os.Remove("libgo4.a")
   850  			os.Remove("libgo4.h")
   851  			os.Remove("testar")
   852  			os.Remove("testar.ran")
   853  			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
   854  		}()
   855  	}
   856  
   857  	os.Remove("testar")
   858  	dir, err := os.Getwd()
   859  	if err != nil {
   860  		t.Fatal(err)
   861  	}
   862  	s := strings.Replace(testar, "PWD", dir, 1)
   863  	if err := os.WriteFile("testar", []byte(s), 0777); err != nil {
   864  		t.Fatal(err)
   865  	}
   866  
   867  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "./libgo4")
   868  	if out, err := cmd.CombinedOutput(); err != nil {
   869  		t.Logf("%s", out)
   870  		t.Fatal(err)
   871  	}
   872  	checkLineComments(t, "libgo4.h")
   873  
   874  	if _, err := os.Stat("testar.ran"); err != nil {
   875  		if os.IsNotExist(err) {
   876  			t.Error("testar does not exist after go build")
   877  		} else {
   878  			t.Errorf("error checking testar: %v", err)
   879  		}
   880  	}
   881  }
   882  
   883  func TestPIE(t *testing.T) {
   884  	switch GOOS {
   885  	case "windows", "darwin", "ios", "plan9":
   886  		t.Skipf("skipping PIE test on %s", GOOS)
   887  	}
   888  
   889  	libgoa := "libgo.a"
   890  	if runtime.Compiler == "gccgo" {
   891  		libgoa = "liblibgo.a"
   892  	}
   893  
   894  	if !testWork {
   895  		defer func() {
   896  			os.Remove("testp" + exeSuffix)
   897  			os.Remove(libgoa)
   898  			os.RemoveAll(filepath.Join(GOPATH, "pkg"))
   899  		}()
   900  	}
   901  
   902  	// Generate the p.h header file.
   903  	//
   904  	// 'go install -i -buildmode=c-archive ./libgo' would do that too, but that
   905  	// would also attempt to install transitive standard-library dependencies to
   906  	// GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may
   907  	// be running this test in a GOROOT owned by root.)
   908  	genHeader(t, "p.h", "./p")
   909  
   910  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "./libgo")
   911  	if out, err := cmd.CombinedOutput(); err != nil {
   912  		t.Logf("%s", out)
   913  		t.Fatal(err)
   914  	}
   915  
   916  	ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", libgoa)
   917  	if runtime.Compiler == "gccgo" {
   918  		ccArgs = append(ccArgs, "-lgo")
   919  	}
   920  	if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
   921  		t.Logf("%s", out)
   922  		t.Fatal(err)
   923  	}
   924  
   925  	binArgs := append(bin, "arg1", "arg2")
   926  	cmd = exec.Command(binArgs[0], binArgs[1:]...)
   927  	if runtime.Compiler == "gccgo" {
   928  		cmd.Env = append(os.Environ(), "GCCGO=1")
   929  	}
   930  	if out, err := cmd.CombinedOutput(); err != nil {
   931  		t.Logf("%s", out)
   932  		t.Fatal(err)
   933  	}
   934  
   935  	if GOOS != "aix" {
   936  		f, err := elf.Open("testp" + exeSuffix)
   937  		if err != nil {
   938  			t.Fatal("elf.Open failed: ", err)
   939  		}
   940  		defer f.Close()
   941  		if hasDynTag(t, f, elf.DT_TEXTREL) {
   942  			t.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix)
   943  		}
   944  	}
   945  }
   946  
   947  func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool {
   948  	ds := f.SectionByType(elf.SHT_DYNAMIC)
   949  	if ds == nil {
   950  		t.Error("no SHT_DYNAMIC section")
   951  		return false
   952  	}
   953  	d, err := ds.Data()
   954  	if err != nil {
   955  		t.Errorf("can't read SHT_DYNAMIC contents: %v", err)
   956  		return false
   957  	}
   958  	for len(d) > 0 {
   959  		var t elf.DynTag
   960  		switch f.Class {
   961  		case elf.ELFCLASS32:
   962  			t = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
   963  			d = d[8:]
   964  		case elf.ELFCLASS64:
   965  			t = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
   966  			d = d[16:]
   967  		}
   968  		if t == tag {
   969  			return true
   970  		}
   971  	}
   972  	return false
   973  }
   974  
   975  func TestSIGPROF(t *testing.T) {
   976  	switch GOOS {
   977  	case "windows", "plan9":
   978  		t.Skipf("skipping SIGPROF test on %s", GOOS)
   979  	case "darwin", "ios":
   980  		t.Skipf("skipping SIGPROF test on %s; see https://golang.org/issue/19320", GOOS)
   981  	}
   982  
   983  	t.Parallel()
   984  
   985  	if !testWork {
   986  		defer func() {
   987  			os.Remove("testp6" + exeSuffix)
   988  			os.Remove("libgo6.a")
   989  			os.Remove("libgo6.h")
   990  		}()
   991  	}
   992  
   993  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6")
   994  	out, err := cmd.CombinedOutput()
   995  	t.Logf("%v\n%s", cmd.Args, out)
   996  	if err != nil {
   997  		t.Fatal(err)
   998  	}
   999  	checkLineComments(t, "libgo6.h")
  1000  	checkArchive(t, "libgo6.a")
  1001  
  1002  	ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a")
  1003  	if runtime.Compiler == "gccgo" {
  1004  		ccArgs = append(ccArgs, "-lgo")
  1005  	}
  1006  	out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
  1007  	t.Logf("%v\n%s", ccArgs, out)
  1008  	if err != nil {
  1009  		t.Fatal(err)
  1010  	}
  1011  
  1012  	argv := cmdToRun("./testp6")
  1013  	cmd = exec.Command(argv[0], argv[1:]...)
  1014  	out, err = cmd.CombinedOutput()
  1015  	t.Logf("%v\n%s", argv, out)
  1016  	if err != nil {
  1017  		t.Fatal(err)
  1018  	}
  1019  }
  1020  
  1021  // TestCompileWithoutShared tests that if we compile code without the
  1022  // -shared option, we can put it into an archive. When we use the go
  1023  // tool with -buildmode=c-archive, it passes -shared to the compiler,
  1024  // so we override that. The go tool doesn't work this way, but Bazel
  1025  // will likely do it in the future. And it ought to work. This test
  1026  // was added because at one time it did not work on PPC Linux.
  1027  func TestCompileWithoutShared(t *testing.T) {
  1028  	// For simplicity, reuse the signal forwarding test.
  1029  	checkSignalForwardingTest(t)
  1030  
  1031  	if !testWork {
  1032  		defer func() {
  1033  			os.Remove("libgo2.a")
  1034  			os.Remove("libgo2.h")
  1035  		}()
  1036  	}
  1037  
  1038  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2")
  1039  	out, err := cmd.CombinedOutput()
  1040  	t.Logf("%v\n%s", cmd.Args, out)
  1041  	if err != nil {
  1042  		t.Fatal(err)
  1043  	}
  1044  	checkLineComments(t, "libgo2.h")
  1045  	checkArchive(t, "libgo2.a")
  1046  
  1047  	exe := "./testnoshared" + exeSuffix
  1048  
  1049  	// In some cases, -no-pie is needed here, but not accepted everywhere. First try
  1050  	// if -no-pie is accepted. See #22126.
  1051  	ccArgs := append(cc, "-o", exe, "-no-pie", "main5.c", "libgo2.a")
  1052  	if runtime.Compiler == "gccgo" {
  1053  		ccArgs = append(ccArgs, "-lgo")
  1054  	}
  1055  	out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
  1056  	t.Logf("%v\n%s", ccArgs, out)
  1057  
  1058  	// If -no-pie unrecognized, try -nopie if this is possibly clang
  1059  	if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") {
  1060  		ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a")
  1061  		out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
  1062  		t.Logf("%v\n%s", ccArgs, out)
  1063  	}
  1064  
  1065  	// Don't use either -no-pie or -nopie
  1066  	if err != nil && bytes.Contains(out, []byte("unrecognized")) {
  1067  		ccArgs = append(cc, "-o", exe, "main5.c", "libgo2.a")
  1068  		out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
  1069  		t.Logf("%v\n%s", ccArgs, out)
  1070  	}
  1071  	if err != nil {
  1072  		t.Fatal(err)
  1073  	}
  1074  	if !testWork {
  1075  		defer os.Remove(exe)
  1076  	}
  1077  
  1078  	binArgs := append(cmdToRun(exe), "1")
  1079  	out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
  1080  	t.Logf("%v\n%s", binArgs, out)
  1081  	expectSignal(t, err, syscall.SIGSEGV, 0)
  1082  
  1083  	// SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
  1084  	if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
  1085  		binArgs := append(cmdToRun(exe), "3")
  1086  		out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
  1087  		t.Logf("%v\n%s", binArgs, out)
  1088  		expectSignal(t, err, syscall.SIGPIPE, 0)
  1089  	}
  1090  }
  1091  
  1092  // Test that installing a second time recreates the header file.
  1093  func TestCachedInstall(t *testing.T) {
  1094  	if !testWork {
  1095  		defer os.RemoveAll(filepath.Join(GOPATH, "pkg"))
  1096  	}
  1097  
  1098  	h := filepath.Join(libgodir, "libgo.h")
  1099  
  1100  	buildcmd := []string{"go", "install", "-buildmode=c-archive", "./libgo"}
  1101  
  1102  	cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
  1103  	cmd.Env = append(cmd.Environ(), "GO111MODULE=off") // 'go install' only works in GOPATH mode
  1104  	t.Log(buildcmd)
  1105  	if out, err := cmd.CombinedOutput(); err != nil {
  1106  		t.Logf("%s", out)
  1107  		t.Fatal(err)
  1108  	}
  1109  
  1110  	if _, err := os.Stat(h); err != nil {
  1111  		t.Errorf("libgo.h not installed: %v", err)
  1112  	}
  1113  
  1114  	if err := os.Remove(h); err != nil {
  1115  		t.Fatal(err)
  1116  	}
  1117  
  1118  	cmd = exec.Command(buildcmd[0], buildcmd[1:]...)
  1119  	cmd.Env = append(cmd.Environ(), "GO111MODULE=off")
  1120  	t.Log(buildcmd)
  1121  	if out, err := cmd.CombinedOutput(); err != nil {
  1122  		t.Logf("%s", out)
  1123  		t.Fatal(err)
  1124  	}
  1125  
  1126  	if _, err := os.Stat(h); err != nil {
  1127  		t.Errorf("libgo.h not installed in second run: %v", err)
  1128  	}
  1129  }
  1130  
  1131  // Issue 35294.
  1132  func TestManyCalls(t *testing.T) {
  1133  	t.Parallel()
  1134  
  1135  	if !testWork {
  1136  		defer func() {
  1137  			os.Remove("testp7" + exeSuffix)
  1138  			os.Remove("libgo7.a")
  1139  			os.Remove("libgo7.h")
  1140  		}()
  1141  	}
  1142  
  1143  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7")
  1144  	out, err := cmd.CombinedOutput()
  1145  	t.Logf("%v\n%s", cmd.Args, out)
  1146  	if err != nil {
  1147  		t.Fatal(err)
  1148  	}
  1149  	checkLineComments(t, "libgo7.h")
  1150  	checkArchive(t, "libgo7.a")
  1151  
  1152  	ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a")
  1153  	if runtime.Compiler == "gccgo" {
  1154  		ccArgs = append(ccArgs, "-lgo")
  1155  	}
  1156  	out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
  1157  	t.Logf("%v\n%s", ccArgs, out)
  1158  	if err != nil {
  1159  		t.Fatal(err)
  1160  	}
  1161  
  1162  	argv := cmdToRun("./testp7")
  1163  	cmd = exec.Command(argv[0], argv[1:]...)
  1164  	sb := new(strings.Builder)
  1165  	cmd.Stdout = sb
  1166  	cmd.Stderr = sb
  1167  	if err := cmd.Start(); err != nil {
  1168  		t.Fatal(err)
  1169  	}
  1170  
  1171  	timer := time.AfterFunc(time.Minute,
  1172  		func() {
  1173  			t.Error("test program timed out")
  1174  			cmd.Process.Kill()
  1175  		},
  1176  	)
  1177  	defer timer.Stop()
  1178  
  1179  	err = cmd.Wait()
  1180  	t.Logf("%v\n%s", cmd.Args, sb)
  1181  	if err != nil {
  1182  		t.Error(err)
  1183  	}
  1184  }
  1185  
  1186  // Issue 49288.
  1187  func TestPreemption(t *testing.T) {
  1188  	if runtime.Compiler == "gccgo" {
  1189  		t.Skip("skipping asynchronous preemption test with gccgo")
  1190  	}
  1191  
  1192  	t.Parallel()
  1193  
  1194  	if !testWork {
  1195  		defer func() {
  1196  			os.Remove("testp8" + exeSuffix)
  1197  			os.Remove("libgo8.a")
  1198  			os.Remove("libgo8.h")
  1199  		}()
  1200  	}
  1201  
  1202  	cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8")
  1203  	out, err := cmd.CombinedOutput()
  1204  	t.Logf("%v\n%s", cmd.Args, out)
  1205  	if err != nil {
  1206  		t.Fatal(err)
  1207  	}
  1208  	checkLineComments(t, "libgo8.h")
  1209  	checkArchive(t, "libgo8.a")
  1210  
  1211  	ccArgs := append(cc, "-o", "testp8"+exeSuffix, "main8.c", "libgo8.a")
  1212  	out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
  1213  	t.Logf("%v\n%s", ccArgs, out)
  1214  	if err != nil {
  1215  		t.Fatal(err)
  1216  	}
  1217  
  1218  	argv := cmdToRun("./testp8")
  1219  	cmd = exec.Command(argv[0], argv[1:]...)
  1220  	sb := new(strings.Builder)
  1221  	cmd.Stdout = sb
  1222  	cmd.Stderr = sb
  1223  	if err := cmd.Start(); err != nil {
  1224  		t.Fatal(err)
  1225  	}
  1226  
  1227  	timer := time.AfterFunc(time.Minute,
  1228  		func() {
  1229  			t.Error("test program timed out")
  1230  			cmd.Process.Kill()
  1231  		},
  1232  	)
  1233  	defer timer.Stop()
  1234  
  1235  	err = cmd.Wait()
  1236  	t.Logf("%v\n%s", cmd.Args, sb)
  1237  	if err != nil {
  1238  		t.Error(err)
  1239  	}
  1240  }