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