...

Source file src/cmd/cgo/internal/testcarchive/carchive_test.go

Documentation: cmd/cgo/internal/testcarchive

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

View as plain text