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