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