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 5package carchive_test 6 7import ( 8 "bufio" 9 "bytes" 10 "debug/elf" 11 "flag" 12 "fmt" 13 "io/ioutil" 14 "log" 15 "os" 16 "os/exec" 17 "path/filepath" 18 "regexp" 19 "runtime" 20 "strings" 21 "syscall" 22 "testing" 23 "time" 24 "unicode" 25) 26 27// Program to run. 28var bin []string 29 30// C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)). 31var cc []string 32 33// ".exe" on Windows. 34var exeSuffix string 35 36var GOOS, GOARCH, GOPATH string 37var libgodir string 38 39var testWork bool // If true, preserve temporary directories. 40 41func TestMain(m *testing.M) { 42 flag.BoolVar(&testWork, "testwork", false, "if true, log and preserve the test's temporary working directory") 43 flag.Parse() 44 if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" { 45 fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n") 46 os.Exit(0) 47 } 48 log.SetFlags(log.Lshortfile) 49 os.Exit(testMain(m)) 50} 51 52func testMain(m *testing.M) int { 53 // We need a writable GOPATH in which to run the tests. 54 // Construct one in a temporary directory. 55 var err error 56 GOPATH, err = ioutil.TempDir("", "carchive_test") 57 if err != nil { 58 log.Panic(err) 59 } 60 if testWork { 61 log.Println(GOPATH) 62 } else { 63 defer os.RemoveAll(GOPATH) 64 } 65 os.Setenv("GOPATH", GOPATH) 66 67 // Copy testdata into GOPATH/src/testarchive, along with a go.mod file 68 // declaring the same path. 69 modRoot := filepath.Join(GOPATH, "src", "testcarchive") 70 if err := overlayDir(modRoot, "testdata"); err != nil { 71 log.Panic(err) 72 } 73 if err := os.Chdir(modRoot); err != nil { 74 log.Panic(err) 75 } 76 os.Setenv("PWD", modRoot) 77 if err := ioutil.WriteFile("go.mod", []byte("module testcarchive\n"), 0666); err != nil { 78 log.Panic(err) 79 } 80 81 GOOS = goEnv("GOOS") 82 GOARCH = goEnv("GOARCH") 83 bin = cmdToRun("./testp") 84 85 ccOut := goEnv("CC") 86 cc = []string{string(ccOut)} 87 88 out := goEnv("GOGCCFLAGS") 89 quote := '\000' 90 start := 0 91 lastSpace := true 92 backslash := false 93 s := string(out) 94 for i, c := range s { 95 if quote == '\000' && unicode.IsSpace(c) { 96 if !lastSpace { 97 cc = append(cc, s[start:i]) 98 lastSpace = true 99 } 100 } else { 101 if lastSpace { 102 start = i 103 lastSpace = false 104 } 105 if quote == '\000' && !backslash && (c == '"' || c == '\'') { 106 quote = c 107 backslash = false 108 } else if !backslash && quote == c { 109 quote = '\000' 110 } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' { 111 backslash = true 112 } else { 113 backslash = false 114 } 115 } 116 } 117 if !lastSpace { 118 cc = append(cc, s[start:]) 119 } 120 121 if GOOS == "darwin" { 122 // For Darwin/ARM. 123 // TODO(crawshaw): can we do better? 124 cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...) 125 } 126 if GOOS == "aix" { 127 // -Wl,-bnoobjreorder is mandatory to keep the same layout 128 // in .text section. 129 cc = append(cc, "-Wl,-bnoobjreorder") 130 } 131 libbase := GOOS + "_" + GOARCH 132 if runtime.Compiler == "gccgo" { 133 libbase = "gccgo_" + libbase + "_fPIC" 134 } else { 135 switch GOOS { 136 case "darwin": 137 if GOARCH == "arm" || GOARCH == "arm64" { 138 libbase += "_shared" 139 } 140 case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris", "illumos": 141 libbase += "_shared" 142 } 143 } 144 libgodir = filepath.Join(GOPATH, "pkg", libbase, "testcarchive") 145 cc = append(cc, "-I", libgodir) 146 147 if GOOS == "windows" { 148 exeSuffix = ".exe" 149 } 150 151 return m.Run() 152} 153 154func goEnv(key string) string { 155 out, err := exec.Command("go", "env", key).Output() 156 if err != nil { 157 if ee, ok := err.(*exec.ExitError); ok { 158 fmt.Fprintf(os.Stderr, "%s", ee.Stderr) 159 } 160 log.Panicf("go env %s failed:\n%s\n", key, err) 161 } 162 return strings.TrimSpace(string(out)) 163} 164 165func cmdToRun(name string) []string { 166 execScript := "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec" 167 executor, err := exec.LookPath(execScript) 168 if err != nil { 169 return []string{name} 170 } 171 return []string{executor, name} 172} 173 174// genHeader writes a C header file for the C-exported declarations found in .go 175// source files in dir. 176// 177// TODO(golang.org/issue/35715): This should be simpler. 178func genHeader(t *testing.T, header, dir string) { 179 t.Helper() 180 181 // The 'cgo' command generates a number of additional artifacts, 182 // but we're only interested in the header. 183 // Shunt the rest of the outputs to a temporary directory. 184 objDir, err := ioutil.TempDir(GOPATH, "_obj") 185 if err != nil { 186 t.Fatal(err) 187 } 188 defer os.RemoveAll(objDir) 189 190 files, err := filepath.Glob(filepath.Join(dir, "*.go")) 191 if err != nil { 192 t.Fatal(err) 193 } 194 195 cmd := exec.Command("go", "tool", "cgo", 196 "-objdir", objDir, 197 "-exportheader", header) 198 cmd.Args = append(cmd.Args, files...) 199 t.Log(cmd.Args) 200 if out, err := cmd.CombinedOutput(); err != nil { 201 t.Logf("%s", out) 202 t.Fatal(err) 203 } 204} 205 206func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) { 207 t.Helper() 208 cmd := exec.Command(buildcmd[0], buildcmd[1:]...) 209 t.Log(buildcmd) 210 if out, err := cmd.CombinedOutput(); err != nil { 211 t.Logf("%s", out) 212 t.Fatal(err) 213 } 214 if !testWork { 215 defer func() { 216 os.Remove(libgoa) 217 os.Remove(libgoh) 218 }() 219 } 220 221 ccArgs := append(cc, "-o", exe, "main.c") 222 if GOOS == "windows" { 223 ccArgs = append(ccArgs, "main_windows.c", libgoa, "-lntdll", "-lws2_32", "-lwinmm") 224 } else { 225 ccArgs = append(ccArgs, "main_unix.c", libgoa) 226 } 227 if runtime.Compiler == "gccgo" { 228 ccArgs = append(ccArgs, "-lgo") 229 } 230 t.Log(ccArgs) 231 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 232 t.Logf("%s", out) 233 t.Fatal(err) 234 } 235 if !testWork { 236 defer os.Remove(exe) 237 } 238 239 binArgs := append(cmdToRun(exe), "arg1", "arg2") 240 cmd = exec.Command(binArgs[0], binArgs[1:]...) 241 if runtime.Compiler == "gccgo" { 242 cmd.Env = append(os.Environ(), "GCCGO=1") 243 } 244 if out, err := cmd.CombinedOutput(); err != nil { 245 t.Logf("%s", out) 246 t.Fatal(err) 247 } 248 249 checkLineComments(t, libgoh) 250} 251 252var badLineRegexp = regexp.MustCompile(`(?m)^#line [0-9]+ "/.*$`) 253 254// checkLineComments checks that the export header generated by 255// -buildmode=c-archive doesn't have any absolute paths in the #line 256// comments. We don't want those paths because they are unhelpful for 257// the user and make the files change based on details of the location 258// of GOPATH. 259func checkLineComments(t *testing.T, hdrname string) { 260 hdr, err := ioutil.ReadFile(hdrname) 261 if err != nil { 262 if !os.IsNotExist(err) { 263 t.Error(err) 264 } 265 return 266 } 267 if line := badLineRegexp.Find(hdr); line != nil { 268 t.Errorf("bad #line directive with absolute path in %s: %q", hdrname, line) 269 } 270} 271 272func TestInstall(t *testing.T) { 273 if !testWork { 274 defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) 275 } 276 277 libgoa := "libgo.a" 278 if runtime.Compiler == "gccgo" { 279 libgoa = "liblibgo.a" 280 } 281 282 // Generate the p.h header file. 283 // 284 // 'go install -i -buildmode=c-archive ./libgo' would do that too, but that 285 // would also attempt to install transitive standard-library dependencies to 286 // GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may 287 // be running this test in a GOROOT owned by root.) 288 genHeader(t, "p.h", "./p") 289 290 testInstall(t, "./testp1"+exeSuffix, 291 filepath.Join(libgodir, libgoa), 292 filepath.Join(libgodir, "libgo.h"), 293 "go", "install", "-buildmode=c-archive", "./libgo") 294 295 // Test building libgo other than installing it. 296 // Header files are now present. 297 testInstall(t, "./testp2"+exeSuffix, "libgo.a", "libgo.h", 298 "go", "build", "-buildmode=c-archive", filepath.Join(".", "libgo", "libgo.go")) 299 300 testInstall(t, "./testp3"+exeSuffix, "libgo.a", "libgo.h", 301 "go", "build", "-buildmode=c-archive", "-o", "libgo.a", "./libgo") 302} 303 304func TestEarlySignalHandler(t *testing.T) { 305 switch GOOS { 306 case "darwin": 307 switch GOARCH { 308 case "arm", "arm64": 309 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) 310 } 311 case "windows": 312 t.Skip("skipping signal test on Windows") 313 } 314 315 if !testWork { 316 defer func() { 317 os.Remove("libgo2.a") 318 os.Remove("libgo2.h") 319 os.Remove("testp") 320 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 321 }() 322 } 323 324 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") 325 if out, err := cmd.CombinedOutput(); err != nil { 326 t.Logf("%s", out) 327 t.Fatal(err) 328 } 329 checkLineComments(t, "libgo2.h") 330 331 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a") 332 if runtime.Compiler == "gccgo" { 333 ccArgs = append(ccArgs, "-lgo") 334 } 335 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 336 t.Logf("%s", out) 337 t.Fatal(err) 338 } 339 340 darwin := "0" 341 if runtime.GOOS == "darwin" { 342 darwin = "1" 343 } 344 cmd = exec.Command(bin[0], append(bin[1:], darwin)...) 345 346 if out, err := cmd.CombinedOutput(); err != nil { 347 t.Logf("%s", out) 348 t.Fatal(err) 349 } 350} 351 352func TestSignalForwarding(t *testing.T) { 353 checkSignalForwardingTest(t) 354 355 if !testWork { 356 defer func() { 357 os.Remove("libgo2.a") 358 os.Remove("libgo2.h") 359 os.Remove("testp") 360 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 361 }() 362 } 363 364 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") 365 if out, err := cmd.CombinedOutput(); err != nil { 366 t.Logf("%s", out) 367 t.Fatal(err) 368 } 369 checkLineComments(t, "libgo2.h") 370 371 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") 372 if runtime.Compiler == "gccgo" { 373 ccArgs = append(ccArgs, "-lgo") 374 } 375 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 376 t.Logf("%s", out) 377 t.Fatal(err) 378 } 379 380 cmd = exec.Command(bin[0], append(bin[1:], "1")...) 381 382 out, err := cmd.CombinedOutput() 383 t.Logf("%s", out) 384 expectSignal(t, err, syscall.SIGSEGV) 385 386 // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. 387 if runtime.GOOS != "darwin" { 388 // Test SIGPIPE forwarding 389 cmd = exec.Command(bin[0], append(bin[1:], "3")...) 390 391 out, err = cmd.CombinedOutput() 392 t.Logf("%s", out) 393 expectSignal(t, err, syscall.SIGPIPE) 394 } 395} 396 397func TestSignalForwardingExternal(t *testing.T) { 398 if GOOS == "freebsd" || GOOS == "aix" { 399 t.Skipf("skipping on %s/%s; signal always goes to the Go runtime", GOOS, GOARCH) 400 } else if GOOS == "darwin" && GOARCH == "amd64" { 401 t.Skipf("skipping on %s/%s: runtime does not permit SI_USER SIGSEGV", GOOS, GOARCH) 402 } 403 checkSignalForwardingTest(t) 404 405 if !testWork { 406 defer func() { 407 os.Remove("libgo2.a") 408 os.Remove("libgo2.h") 409 os.Remove("testp") 410 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 411 }() 412 } 413 414 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") 415 if out, err := cmd.CombinedOutput(); err != nil { 416 t.Logf("%s", out) 417 t.Fatal(err) 418 } 419 checkLineComments(t, "libgo2.h") 420 421 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") 422 if runtime.Compiler == "gccgo" { 423 ccArgs = append(ccArgs, "-lgo") 424 } 425 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 426 t.Logf("%s", out) 427 t.Fatal(err) 428 } 429 430 // We want to send the process a signal and see if it dies. 431 // Normally the signal goes to the C thread, the Go signal 432 // handler picks it up, sees that it is running in a C thread, 433 // and the program dies. Unfortunately, occasionally the 434 // signal is delivered to a Go thread, which winds up 435 // discarding it because it was sent by another program and 436 // there is no Go handler for it. To avoid this, run the 437 // program several times in the hopes that it will eventually 438 // fail. 439 const tries = 20 440 for i := 0; i < tries; i++ { 441 cmd = exec.Command(bin[0], append(bin[1:], "2")...) 442 443 stderr, err := cmd.StderrPipe() 444 if err != nil { 445 t.Fatal(err) 446 } 447 defer stderr.Close() 448 449 r := bufio.NewReader(stderr) 450 451 err = cmd.Start() 452 453 if err != nil { 454 t.Fatal(err) 455 } 456 457 // Wait for trigger to ensure that the process is started. 458 ok, err := r.ReadString('\n') 459 460 // Verify trigger. 461 if err != nil || ok != "OK\n" { 462 t.Fatalf("Did not receive OK signal") 463 } 464 465 // Give the program a chance to enter the sleep function. 466 time.Sleep(time.Millisecond) 467 468 cmd.Process.Signal(syscall.SIGSEGV) 469 470 err = cmd.Wait() 471 472 if err == nil { 473 continue 474 } 475 476 if expectSignal(t, err, syscall.SIGSEGV) { 477 return 478 } 479 } 480 481 t.Errorf("program succeeded unexpectedly %d times", tries) 482} 483 484// checkSignalForwardingTest calls t.Skip if the SignalForwarding test 485// doesn't work on this platform. 486func checkSignalForwardingTest(t *testing.T) { 487 switch GOOS { 488 case "darwin": 489 switch GOARCH { 490 case "arm", "arm64": 491 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) 492 } 493 case "windows": 494 t.Skip("skipping signal test on Windows") 495 } 496} 497 498// expectSignal checks that err, the exit status of a test program, 499// shows a failure due to a specific signal. Returns whether we found 500// the expected signal. 501func expectSignal(t *testing.T, err error, sig syscall.Signal) bool { 502 if err == nil { 503 t.Error("test program succeeded unexpectedly") 504 } else if ee, ok := err.(*exec.ExitError); !ok { 505 t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) 506 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 507 t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) 508 } else if !ws.Signaled() || ws.Signal() != sig { 509 t.Errorf("got %v; expected signal %v", ee, sig) 510 } else { 511 return true 512 } 513 return false 514} 515 516func TestOsSignal(t *testing.T) { 517 switch GOOS { 518 case "windows": 519 t.Skip("skipping signal test on Windows") 520 } 521 522 if !testWork { 523 defer func() { 524 os.Remove("libgo3.a") 525 os.Remove("libgo3.h") 526 os.Remove("testp") 527 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 528 }() 529 } 530 531 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "./libgo3") 532 if out, err := cmd.CombinedOutput(); err != nil { 533 t.Logf("%s", out) 534 t.Fatal(err) 535 } 536 checkLineComments(t, "libgo3.h") 537 538 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a") 539 if runtime.Compiler == "gccgo" { 540 ccArgs = append(ccArgs, "-lgo") 541 } 542 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 543 t.Logf("%s", out) 544 t.Fatal(err) 545 } 546 547 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil { 548 t.Logf("%s", out) 549 t.Fatal(err) 550 } 551} 552 553func TestSigaltstack(t *testing.T) { 554 switch GOOS { 555 case "windows": 556 t.Skip("skipping signal test on Windows") 557 } 558 559 if !testWork { 560 defer func() { 561 os.Remove("libgo4.a") 562 os.Remove("libgo4.h") 563 os.Remove("testp") 564 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 565 }() 566 } 567 568 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "./libgo4") 569 if out, err := cmd.CombinedOutput(); err != nil { 570 t.Logf("%s", out) 571 t.Fatal(err) 572 } 573 checkLineComments(t, "libgo4.h") 574 575 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a") 576 if runtime.Compiler == "gccgo" { 577 ccArgs = append(ccArgs, "-lgo") 578 } 579 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 580 t.Logf("%s", out) 581 t.Fatal(err) 582 } 583 584 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil { 585 t.Logf("%s", out) 586 t.Fatal(err) 587 } 588} 589 590const testar = `#!/usr/bin/env bash 591while [[ $1 == -* ]] >/dev/null; do 592 shift 593done 594echo "testar" > $1 595echo "testar" > PWD/testar.ran 596` 597 598func TestExtar(t *testing.T) { 599 switch GOOS { 600 case "windows": 601 t.Skip("skipping signal test on Windows") 602 } 603 if runtime.Compiler == "gccgo" { 604 t.Skip("skipping -extar test when using gccgo") 605 } 606 if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { 607 t.Skip("shell scripts are not executable on iOS hosts") 608 } 609 610 if !testWork { 611 defer func() { 612 os.Remove("libgo4.a") 613 os.Remove("libgo4.h") 614 os.Remove("testar") 615 os.Remove("testar.ran") 616 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 617 }() 618 } 619 620 os.Remove("testar") 621 dir, err := os.Getwd() 622 if err != nil { 623 t.Fatal(err) 624 } 625 s := strings.Replace(testar, "PWD", dir, 1) 626 if err := ioutil.WriteFile("testar", []byte(s), 0777); err != nil { 627 t.Fatal(err) 628 } 629 630 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "./libgo4") 631 if out, err := cmd.CombinedOutput(); err != nil { 632 t.Logf("%s", out) 633 t.Fatal(err) 634 } 635 checkLineComments(t, "libgo4.h") 636 637 if _, err := os.Stat("testar.ran"); err != nil { 638 if os.IsNotExist(err) { 639 t.Error("testar does not exist after go build") 640 } else { 641 t.Errorf("error checking testar: %v", err) 642 } 643 } 644} 645 646func TestPIE(t *testing.T) { 647 switch GOOS { 648 case "windows", "darwin", "plan9": 649 t.Skipf("skipping PIE test on %s", GOOS) 650 } 651 652 if !testWork { 653 defer func() { 654 os.Remove("testp" + exeSuffix) 655 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 656 }() 657 } 658 659 // Generate the p.h header file. 660 // 661 // 'go install -i -buildmode=c-archive ./libgo' would do that too, but that 662 // would also attempt to install transitive standard-library dependencies to 663 // GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may 664 // be running this test in a GOROOT owned by root.) 665 genHeader(t, "p.h", "./p") 666 667 cmd := exec.Command("go", "install", "-buildmode=c-archive", "./libgo") 668 if out, err := cmd.CombinedOutput(); err != nil { 669 t.Logf("%s", out) 670 t.Fatal(err) 671 } 672 673 libgoa := "libgo.a" 674 if runtime.Compiler == "gccgo" { 675 libgoa = "liblibgo.a" 676 } 677 678 ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", filepath.Join(libgodir, libgoa)) 679 if runtime.Compiler == "gccgo" { 680 ccArgs = append(ccArgs, "-lgo") 681 } 682 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 683 t.Logf("%s", out) 684 t.Fatal(err) 685 } 686 687 binArgs := append(bin, "arg1", "arg2") 688 cmd = exec.Command(binArgs[0], binArgs[1:]...) 689 if runtime.Compiler == "gccgo" { 690 cmd.Env = append(os.Environ(), "GCCGO=1") 691 } 692 if out, err := cmd.CombinedOutput(); err != nil { 693 t.Logf("%s", out) 694 t.Fatal(err) 695 } 696 697 if GOOS != "aix" { 698 f, err := elf.Open("testp" + exeSuffix) 699 if err != nil { 700 t.Fatal("elf.Open failed: ", err) 701 } 702 defer f.Close() 703 if hasDynTag(t, f, elf.DT_TEXTREL) { 704 t.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix) 705 } 706 } 707} 708 709func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool { 710 ds := f.SectionByType(elf.SHT_DYNAMIC) 711 if ds == nil { 712 t.Error("no SHT_DYNAMIC section") 713 return false 714 } 715 d, err := ds.Data() 716 if err != nil { 717 t.Errorf("can't read SHT_DYNAMIC contents: %v", err) 718 return false 719 } 720 for len(d) > 0 { 721 var t elf.DynTag 722 switch f.Class { 723 case elf.ELFCLASS32: 724 t = elf.DynTag(f.ByteOrder.Uint32(d[:4])) 725 d = d[8:] 726 case elf.ELFCLASS64: 727 t = elf.DynTag(f.ByteOrder.Uint64(d[:8])) 728 d = d[16:] 729 } 730 if t == tag { 731 return true 732 } 733 } 734 return false 735} 736 737func TestSIGPROF(t *testing.T) { 738 switch GOOS { 739 case "windows", "plan9": 740 t.Skipf("skipping SIGPROF test on %s", GOOS) 741 case "darwin": 742 t.Skipf("skipping SIGPROF test on %s; see https://golang.org/issue/19320", GOOS) 743 } 744 745 t.Parallel() 746 747 if !testWork { 748 defer func() { 749 os.Remove("testp6" + exeSuffix) 750 os.Remove("libgo6.a") 751 os.Remove("libgo6.h") 752 }() 753 } 754 755 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6") 756 if out, err := cmd.CombinedOutput(); err != nil { 757 t.Logf("%s", out) 758 t.Fatal(err) 759 } 760 checkLineComments(t, "libgo6.h") 761 762 ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a") 763 if runtime.Compiler == "gccgo" { 764 ccArgs = append(ccArgs, "-lgo") 765 } 766 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 767 t.Logf("%s", out) 768 t.Fatal(err) 769 } 770 771 argv := cmdToRun("./testp6") 772 cmd = exec.Command(argv[0], argv[1:]...) 773 if out, err := cmd.CombinedOutput(); err != nil { 774 t.Logf("%s", out) 775 t.Fatal(err) 776 } 777} 778 779// TestCompileWithoutShared tests that if we compile code without the 780// -shared option, we can put it into an archive. When we use the go 781// tool with -buildmode=c-archive, it passes -shared to the compiler, 782// so we override that. The go tool doesn't work this way, but Bazel 783// will likely do it in the future. And it ought to work. This test 784// was added because at one time it did not work on PPC GNU/Linux. 785func TestCompileWithoutShared(t *testing.T) { 786 // For simplicity, reuse the signal forwarding test. 787 checkSignalForwardingTest(t) 788 789 if !testWork { 790 defer func() { 791 os.Remove("libgo2.a") 792 os.Remove("libgo2.h") 793 }() 794 } 795 796 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2") 797 t.Log(cmd.Args) 798 out, err := cmd.CombinedOutput() 799 t.Logf("%s", out) 800 if err != nil { 801 t.Fatal(err) 802 } 803 checkLineComments(t, "libgo2.h") 804 805 exe := "./testnoshared" + exeSuffix 806 807 // In some cases, -no-pie is needed here, but not accepted everywhere. First try 808 // if -no-pie is accepted. See #22126. 809 ccArgs := append(cc, "-o", exe, "-no-pie", "main5.c", "libgo2.a") 810 if runtime.Compiler == "gccgo" { 811 ccArgs = append(ccArgs, "-lgo") 812 } 813 t.Log(ccArgs) 814 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() 815 816 // If -no-pie unrecognized, try -nopie if this is possibly clang 817 if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") { 818 ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a") 819 t.Log(ccArgs) 820 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() 821 } 822 823 // Don't use either -no-pie or -nopie 824 if err != nil && bytes.Contains(out, []byte("unrecognized")) { 825 ccArgs := append(cc, "-o", exe, "main5.c", "libgo2.a") 826 t.Log(ccArgs) 827 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() 828 } 829 t.Logf("%s", out) 830 if err != nil { 831 t.Fatal(err) 832 } 833 if !testWork { 834 defer os.Remove(exe) 835 } 836 837 binArgs := append(cmdToRun(exe), "1") 838 t.Log(binArgs) 839 out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() 840 t.Logf("%s", out) 841 expectSignal(t, err, syscall.SIGSEGV) 842 843 // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. 844 if runtime.GOOS != "darwin" { 845 binArgs := append(cmdToRun(exe), "3") 846 t.Log(binArgs) 847 out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() 848 t.Logf("%s", out) 849 expectSignal(t, err, syscall.SIGPIPE) 850 } 851} 852 853// Test that installing a second time recreates the header file. 854func TestCachedInstall(t *testing.T) { 855 if !testWork { 856 defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) 857 } 858 859 h := filepath.Join(libgodir, "libgo.h") 860 861 buildcmd := []string{"go", "install", "-buildmode=c-archive", "./libgo"} 862 863 cmd := exec.Command(buildcmd[0], buildcmd[1:]...) 864 t.Log(buildcmd) 865 if out, err := cmd.CombinedOutput(); err != nil { 866 t.Logf("%s", out) 867 t.Fatal(err) 868 } 869 870 if _, err := os.Stat(h); err != nil { 871 t.Errorf("libgo.h not installed: %v", err) 872 } 873 874 if err := os.Remove(h); err != nil { 875 t.Fatal(err) 876 } 877 878 cmd = exec.Command(buildcmd[0], buildcmd[1:]...) 879 t.Log(buildcmd) 880 if out, err := cmd.CombinedOutput(); err != nil { 881 t.Logf("%s", out) 882 t.Fatal(err) 883 } 884 885 if _, err := os.Stat(h); err != nil { 886 t.Errorf("libgo.h not installed in second run: %v", err) 887 } 888} 889 890// Issue 35294. 891func TestManyCalls(t *testing.T) { 892 t.Parallel() 893 894 if !testWork { 895 defer func() { 896 os.Remove("testp7" + exeSuffix) 897 os.Remove("libgo7.a") 898 os.Remove("libgo7.h") 899 }() 900 } 901 902 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7") 903 if out, err := cmd.CombinedOutput(); err != nil { 904 t.Logf("%s", out) 905 t.Fatal(err) 906 } 907 checkLineComments(t, "libgo7.h") 908 909 ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a") 910 if runtime.Compiler == "gccgo" { 911 ccArgs = append(ccArgs, "-lgo") 912 } 913 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 914 t.Logf("%s", out) 915 t.Fatal(err) 916 } 917 918 argv := cmdToRun("./testp7") 919 cmd = exec.Command(argv[0], argv[1:]...) 920 var sb strings.Builder 921 cmd.Stdout = &sb 922 cmd.Stderr = &sb 923 if err := cmd.Start(); err != nil { 924 t.Fatal(err) 925 } 926 927 timer := time.AfterFunc(time.Minute, 928 func() { 929 t.Error("test program timed out") 930 cmd.Process.Kill() 931 }, 932 ) 933 defer timer.Stop() 934 935 if err := cmd.Wait(); err != nil { 936 t.Log(sb.String()) 937 t.Error(err) 938 } 939} 940