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