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