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
10package carchive_test
11
12import (
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
36var globalSkip = func(t *testing.T) {}
37
38// Program to run.
39var bin []string
40
41// C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
42var cc []string
43
44// ".exe" on Windows.
45var exeSuffix string
46
47var GOOS, GOARCH, GOPATH string
48var libgodir string
49
50var testWork bool // If true, preserve temporary directories.
51
52func 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
60func 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
180func 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
191func 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.
204func 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
232func 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
279var 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.
286func 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.
301func 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.
316func 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.
431func 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
466func 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
503func 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
556func 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
580func 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
629func 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.
649func 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.
666func 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
703func 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.
765func 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
785func 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
827func 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
869const testar = `#!/usr/bin/env bash
870while [[ $1 == -* ]] >/dev/null; do
871  shift
872done
873echo "testar" > $1
874echo "testar" > PWD/testar.ran
875`
876
877func 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
927func 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
995func 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
1023func 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.
1079func 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.
1147func 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.
1191func 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.
1243func 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.
1296func 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
1353func 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