github.com/360nenz/gumt@v0.0.0-20230623171552-8f1e25eda00d/gulang/misc/cgo/testcarchive/carchive_test.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package carchive_test 6 7 import ( 8 "bufio" 9 "bytes" 10 "debug/elf" 11 "flag" 12 "fmt" 13 "io" 14 "log" 15 "os" 16 "os/exec" 17 "path/filepath" 18 "regexp" 19 "runtime" 20 "strconv" 21 "strings" 22 "sync" 23 "syscall" 24 "testing" 25 "time" 26 "unicode" 27 ) 28 29 // Program to run. 30 var bin []string 31 32 // C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)). 33 var cc []string 34 35 // ".exe" on Windows. 36 var exeSuffix string 37 38 var GOOS, GOARCH, GOPATH string 39 var libgodir string 40 41 var testWork bool // If true, preserve temporary directories. 42 43 func TestMain(m *testing.M) { 44 flag.BoolVar(&testWork, "testwork", false, "if true, log and preserve the test's temporary working directory") 45 flag.Parse() 46 if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" { 47 fmt.Printf("SKIP - short mode and $GO_BUILDER_NAME not set\n") 48 os.Exit(0) 49 } 50 if runtime.GOOS == "linux" { 51 if _, err := os.Stat("/etc/alpine-release"); err == nil { 52 fmt.Printf("SKIP - skipping failing test on alpine - go.dev/issue/19938\n") 53 os.Exit(0) 54 } 55 } 56 57 log.SetFlags(log.Lshortfile) 58 os.Exit(testMain(m)) 59 } 60 61 func testMain(m *testing.M) int { 62 // We need a writable GOPATH in which to run the tests. 63 // Construct one in a temporary directory. 64 var err error 65 GOPATH, err = os.MkdirTemp("", "carchive_test") 66 if err != nil { 67 log.Panic(err) 68 } 69 if testWork { 70 log.Println(GOPATH) 71 } else { 72 defer os.RemoveAll(GOPATH) 73 } 74 os.Setenv("GOPATH", GOPATH) 75 76 // Copy testdata into GOPATH/src/testarchive, along with a go.mod file 77 // declaring the same path. 78 modRoot := filepath.Join(GOPATH, "src", "testcarchive") 79 if err := overlayDir(modRoot, "testdata"); err != nil { 80 log.Panic(err) 81 } 82 if err := os.Chdir(modRoot); err != nil { 83 log.Panic(err) 84 } 85 os.Setenv("PWD", modRoot) 86 if err := os.WriteFile("go.mod", []byte("module testcarchive\n"), 0666); err != nil { 87 log.Panic(err) 88 } 89 90 GOOS = goEnv("GOOS") 91 GOARCH = goEnv("GOARCH") 92 bin = cmdToRun("./testp") 93 94 ccOut := goEnv("CC") 95 cc = []string{string(ccOut)} 96 97 out := goEnv("GOGCCFLAGS") 98 quote := '\000' 99 start := 0 100 lastSpace := true 101 backslash := false 102 s := string(out) 103 for i, c := range s { 104 if quote == '\000' && unicode.IsSpace(c) { 105 if !lastSpace { 106 cc = append(cc, s[start:i]) 107 lastSpace = true 108 } 109 } else { 110 if lastSpace { 111 start = i 112 lastSpace = false 113 } 114 if quote == '\000' && !backslash && (c == '"' || c == '\'') { 115 quote = c 116 backslash = false 117 } else if !backslash && quote == c { 118 quote = '\000' 119 } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' { 120 backslash = true 121 } else { 122 backslash = false 123 } 124 } 125 } 126 if !lastSpace { 127 cc = append(cc, s[start:]) 128 } 129 130 if GOOS == "aix" { 131 // -Wl,-bnoobjreorder is mandatory to keep the same layout 132 // in .text section. 133 cc = append(cc, "-Wl,-bnoobjreorder") 134 } 135 libbase := GOOS + "_" + GOARCH 136 if runtime.Compiler == "gccgo" { 137 libbase = "gccgo_" + libgodir + "_fPIC" 138 } else { 139 switch GOOS { 140 case "darwin", "ios": 141 if GOARCH == "arm64" { 142 libbase += "_shared" 143 } 144 case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris", "illumos": 145 libbase += "_shared" 146 } 147 } 148 libgodir = filepath.Join(GOPATH, "pkg", libbase, "testcarchive") 149 cc = append(cc, "-I", libgodir) 150 151 // Force reallocation (and avoid aliasing bugs) for parallel tests that append to cc. 152 cc = cc[:len(cc):len(cc)] 153 154 if GOOS == "windows" { 155 exeSuffix = ".exe" 156 } 157 158 return m.Run() 159 } 160 161 func goEnv(key string) string { 162 out, err := exec.Command("go", "env", key).Output() 163 if err != nil { 164 if ee, ok := err.(*exec.ExitError); ok { 165 fmt.Fprintf(os.Stderr, "%s", ee.Stderr) 166 } 167 log.Panicf("go env %s failed:\n%s\n", key, err) 168 } 169 return strings.TrimSpace(string(out)) 170 } 171 172 func cmdToRun(name string) []string { 173 execScript := "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec" 174 executor, err := exec.LookPath(execScript) 175 if err != nil { 176 return []string{name} 177 } 178 return []string{executor, name} 179 } 180 181 // genHeader writes a C header file for the C-exported declarations found in .go 182 // source files in dir. 183 // 184 // TODO(golang.org/issue/35715): This should be simpler. 185 func genHeader(t *testing.T, header, dir string) { 186 t.Helper() 187 188 // The 'cgo' command generates a number of additional artifacts, 189 // but we're only interested in the header. 190 // Shunt the rest of the outputs to a temporary directory. 191 objDir, err := os.MkdirTemp(GOPATH, "_obj") 192 if err != nil { 193 t.Fatal(err) 194 } 195 defer os.RemoveAll(objDir) 196 197 files, err := filepath.Glob(filepath.Join(dir, "*.go")) 198 if err != nil { 199 t.Fatal(err) 200 } 201 202 cmd := exec.Command("go", "tool", "cgo", 203 "-objdir", objDir, 204 "-exportheader", header) 205 cmd.Args = append(cmd.Args, files...) 206 t.Log(cmd.Args) 207 if out, err := cmd.CombinedOutput(); err != nil { 208 t.Logf("%s", out) 209 t.Fatal(err) 210 } 211 } 212 213 func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) { 214 t.Helper() 215 cmd := exec.Command(buildcmd[0], buildcmd[1:]...) 216 cmd.Env = append(cmd.Environ(), "GO111MODULE=off") // 'go install' only works in GOPATH mode 217 t.Log(buildcmd) 218 if out, err := cmd.CombinedOutput(); err != nil { 219 t.Logf("%s", out) 220 t.Fatal(err) 221 } 222 if !testWork { 223 defer func() { 224 os.Remove(libgoa) 225 os.Remove(libgoh) 226 }() 227 } 228 229 ccArgs := append(cc, "-o", exe, "main.c") 230 if GOOS == "windows" { 231 ccArgs = append(ccArgs, "main_windows.c", libgoa, "-lntdll", "-lws2_32", "-lwinmm") 232 } else { 233 ccArgs = append(ccArgs, "main_unix.c", libgoa) 234 } 235 if runtime.Compiler == "gccgo" { 236 ccArgs = append(ccArgs, "-lgo") 237 } 238 t.Log(ccArgs) 239 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 240 t.Logf("%s", out) 241 t.Fatal(err) 242 } 243 if !testWork { 244 defer os.Remove(exe) 245 } 246 247 binArgs := append(cmdToRun(exe), "arg1", "arg2") 248 cmd = exec.Command(binArgs[0], binArgs[1:]...) 249 if runtime.Compiler == "gccgo" { 250 cmd.Env = append(cmd.Environ(), "GCCGO=1") 251 } 252 if out, err := cmd.CombinedOutput(); err != nil { 253 t.Logf("%s", out) 254 t.Fatal(err) 255 } 256 257 checkLineComments(t, libgoh) 258 } 259 260 var badLineRegexp = regexp.MustCompile(`(?m)^#line [0-9]+ "/.*$`) 261 262 // checkLineComments checks that the export header generated by 263 // -buildmode=c-archive doesn't have any absolute paths in the #line 264 // comments. We don't want those paths because they are unhelpful for 265 // the user and make the files change based on details of the location 266 // of GOPATH. 267 func checkLineComments(t *testing.T, hdrname string) { 268 hdr, err := os.ReadFile(hdrname) 269 if err != nil { 270 if !os.IsNotExist(err) { 271 t.Error(err) 272 } 273 return 274 } 275 if line := badLineRegexp.Find(hdr); line != nil { 276 t.Errorf("bad #line directive with absolute path in %s: %q", hdrname, line) 277 } 278 } 279 280 // checkArchive verifies that the created library looks OK. 281 // We just check a couple of things now, we can add more checks as needed. 282 func checkArchive(t *testing.T, arname string) { 283 t.Helper() 284 285 switch GOOS { 286 case "aix", "darwin", "ios", "windows": 287 // We don't have any checks for non-ELF libraries yet. 288 if _, err := os.Stat(arname); err != nil { 289 t.Errorf("archive %s does not exist: %v", arname, err) 290 } 291 default: 292 checkELFArchive(t, arname) 293 } 294 } 295 296 // checkELFArchive checks an ELF archive. 297 func checkELFArchive(t *testing.T, arname string) { 298 t.Helper() 299 300 f, err := os.Open(arname) 301 if err != nil { 302 t.Errorf("archive %s does not exist: %v", arname, err) 303 return 304 } 305 defer f.Close() 306 307 // TODO(iant): put these in a shared package? But where? 308 const ( 309 magic = "!<arch>\n" 310 fmag = "`\n" 311 312 namelen = 16 313 datelen = 12 314 uidlen = 6 315 gidlen = 6 316 modelen = 8 317 sizelen = 10 318 fmaglen = 2 319 hdrlen = namelen + datelen + uidlen + gidlen + modelen + sizelen + fmaglen 320 ) 321 322 type arhdr struct { 323 name string 324 date string 325 uid string 326 gid string 327 mode string 328 size string 329 fmag string 330 } 331 332 var magbuf [len(magic)]byte 333 if _, err := io.ReadFull(f, magbuf[:]); err != nil { 334 t.Errorf("%s: archive too short", arname) 335 return 336 } 337 if string(magbuf[:]) != magic { 338 t.Errorf("%s: incorrect archive magic string %q", arname, magbuf) 339 } 340 341 off := int64(len(magic)) 342 for { 343 if off&1 != 0 { 344 var b [1]byte 345 if _, err := f.Read(b[:]); err != nil { 346 if err == io.EOF { 347 break 348 } 349 t.Errorf("%s: error skipping alignment byte at %d: %v", arname, off, err) 350 } 351 off++ 352 } 353 354 var hdrbuf [hdrlen]byte 355 if _, err := io.ReadFull(f, hdrbuf[:]); err != nil { 356 if err == io.EOF { 357 break 358 } 359 t.Errorf("%s: error reading archive header at %d: %v", arname, off, err) 360 return 361 } 362 363 var hdr arhdr 364 hdrslice := hdrbuf[:] 365 set := func(len int, ps *string) { 366 *ps = string(bytes.TrimSpace(hdrslice[:len])) 367 hdrslice = hdrslice[len:] 368 } 369 set(namelen, &hdr.name) 370 set(datelen, &hdr.date) 371 set(uidlen, &hdr.uid) 372 set(gidlen, &hdr.gid) 373 set(modelen, &hdr.mode) 374 set(sizelen, &hdr.size) 375 hdr.fmag = string(hdrslice[:fmaglen]) 376 hdrslice = hdrslice[fmaglen:] 377 if len(hdrslice) != 0 { 378 t.Fatalf("internal error: len(hdrslice) == %d", len(hdrslice)) 379 } 380 381 if hdr.fmag != fmag { 382 t.Errorf("%s: invalid fmagic value %q at %d", arname, hdr.fmag, off) 383 return 384 } 385 386 size, err := strconv.ParseInt(hdr.size, 10, 64) 387 if err != nil { 388 t.Errorf("%s: error parsing size %q at %d: %v", arname, hdr.size, off, err) 389 return 390 } 391 392 off += hdrlen 393 394 switch hdr.name { 395 case "__.SYMDEF", "/", "/SYM64/": 396 // The archive symbol map. 397 case "//", "ARFILENAMES/": 398 // The extended name table. 399 default: 400 // This should be an ELF object. 401 checkELFArchiveObject(t, arname, off, io.NewSectionReader(f, off, size)) 402 } 403 404 off += size 405 if _, err := f.Seek(off, io.SeekStart); err != nil { 406 t.Errorf("%s: failed to seek to %d: %v", arname, off, err) 407 } 408 } 409 } 410 411 // checkELFArchiveObject checks an object in an ELF archive. 412 func checkELFArchiveObject(t *testing.T, arname string, off int64, obj io.ReaderAt) { 413 t.Helper() 414 415 ef, err := elf.NewFile(obj) 416 if err != nil { 417 t.Errorf("%s: failed to open ELF file at %d: %v", arname, off, err) 418 return 419 } 420 defer ef.Close() 421 422 // Verify section types. 423 for _, sec := range ef.Sections { 424 want := elf.SHT_NULL 425 switch sec.Name { 426 case ".text", ".data": 427 want = elf.SHT_PROGBITS 428 case ".bss": 429 want = elf.SHT_NOBITS 430 case ".symtab": 431 want = elf.SHT_SYMTAB 432 case ".strtab": 433 want = elf.SHT_STRTAB 434 case ".init_array": 435 want = elf.SHT_INIT_ARRAY 436 case ".fini_array": 437 want = elf.SHT_FINI_ARRAY 438 case ".preinit_array": 439 want = elf.SHT_PREINIT_ARRAY 440 } 441 if want != elf.SHT_NULL && sec.Type != want { 442 t.Errorf("%s: incorrect section type in elf file at %d for section %q: got %v want %v", arname, off, sec.Name, sec.Type, want) 443 } 444 } 445 } 446 447 func TestInstall(t *testing.T) { 448 if !testWork { 449 defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) 450 } 451 452 libgoa := "libgo.a" 453 if runtime.Compiler == "gccgo" { 454 libgoa = "liblibgo.a" 455 } 456 457 // Generate the p.h header file. 458 // 459 // 'go install -i -buildmode=c-archive ./libgo' would do that too, but that 460 // would also attempt to install transitive standard-library dependencies to 461 // GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may 462 // be running this test in a GOROOT owned by root.) 463 genHeader(t, "p.h", "./p") 464 465 testInstall(t, "./testp1"+exeSuffix, 466 filepath.Join(libgodir, libgoa), 467 filepath.Join(libgodir, "libgo.h"), 468 "go", "install", "-buildmode=c-archive", "./libgo") 469 470 // Test building libgo other than installing it. 471 // Header files are now present. 472 testInstall(t, "./testp2"+exeSuffix, "libgo.a", "libgo.h", 473 "go", "build", "-buildmode=c-archive", filepath.Join(".", "libgo", "libgo.go")) 474 475 testInstall(t, "./testp3"+exeSuffix, "libgo.a", "libgo.h", 476 "go", "build", "-buildmode=c-archive", "-o", "libgo.a", "./libgo") 477 } 478 479 func TestEarlySignalHandler(t *testing.T) { 480 switch GOOS { 481 case "darwin", "ios": 482 switch GOARCH { 483 case "arm64": 484 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) 485 } 486 case "windows": 487 t.Skip("skipping signal test on Windows") 488 } 489 490 if !testWork { 491 defer func() { 492 os.Remove("libgo2.a") 493 os.Remove("libgo2.h") 494 os.Remove("testp" + exeSuffix) 495 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 496 }() 497 } 498 499 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") 500 if out, err := cmd.CombinedOutput(); err != nil { 501 t.Logf("%s", out) 502 t.Fatal(err) 503 } 504 checkLineComments(t, "libgo2.h") 505 checkArchive(t, "libgo2.a") 506 507 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a") 508 if runtime.Compiler == "gccgo" { 509 ccArgs = append(ccArgs, "-lgo") 510 } 511 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 512 t.Logf("%s", out) 513 t.Fatal(err) 514 } 515 516 darwin := "0" 517 if runtime.GOOS == "darwin" { 518 darwin = "1" 519 } 520 cmd = exec.Command(bin[0], append(bin[1:], darwin)...) 521 522 if out, err := cmd.CombinedOutput(); err != nil { 523 t.Logf("%s", out) 524 t.Fatal(err) 525 } 526 } 527 528 func TestSignalForwarding(t *testing.T) { 529 checkSignalForwardingTest(t) 530 buildSignalForwardingTest(t) 531 532 cmd := exec.Command(bin[0], append(bin[1:], "1")...) 533 534 out, err := cmd.CombinedOutput() 535 t.Logf("%v\n%s", cmd.Args, out) 536 expectSignal(t, err, syscall.SIGSEGV, 0) 537 538 // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. 539 if runtime.GOOS != "darwin" && runtime.GOOS != "ios" { 540 // Test SIGPIPE forwarding 541 cmd = exec.Command(bin[0], append(bin[1:], "3")...) 542 543 out, err = cmd.CombinedOutput() 544 if len(out) > 0 { 545 t.Logf("%s", out) 546 } 547 expectSignal(t, err, syscall.SIGPIPE, 0) 548 } 549 } 550 551 func TestSignalForwardingExternal(t *testing.T) { 552 if GOOS == "freebsd" || GOOS == "aix" { 553 t.Skipf("skipping on %s/%s; signal always goes to the Go runtime", GOOS, GOARCH) 554 } else if GOOS == "darwin" && GOARCH == "amd64" { 555 t.Skipf("skipping on %s/%s: runtime does not permit SI_USER SIGSEGV", GOOS, GOARCH) 556 } 557 checkSignalForwardingTest(t) 558 buildSignalForwardingTest(t) 559 560 // We want to send the process a signal and see if it dies. 561 // Normally the signal goes to the C thread, the Go signal 562 // handler picks it up, sees that it is running in a C thread, 563 // and the program dies. Unfortunately, occasionally the 564 // signal is delivered to a Go thread, which winds up 565 // discarding it because it was sent by another program and 566 // there is no Go handler for it. To avoid this, run the 567 // program several times in the hopes that it will eventually 568 // fail. 569 const tries = 20 570 for i := 0; i < tries; i++ { 571 err := runSignalForwardingTest(t, "2") 572 if err == nil { 573 continue 574 } 575 576 // If the signal is delivered to a C thread, as expected, 577 // the Go signal handler will disable itself and re-raise 578 // the signal, causing the program to die with SIGSEGV. 579 // 580 // It is also possible that the signal will be 581 // delivered to a Go thread, such as a GC thread. 582 // Currently when the Go runtime sees that a SIGSEGV was 583 // sent from a different program, it first tries to send 584 // the signal to the os/signal API. If nothing is looking 585 // for (or explicitly ignoring) SIGSEGV, then it crashes. 586 // Because the Go runtime is invoked via a c-archive, 587 // it treats this as GOTRACEBACK=crash, meaning that it 588 // dumps a stack trace for all goroutines, which it does 589 // by raising SIGQUIT. The effect is that we will see the 590 // program die with SIGQUIT in that case, not SIGSEGV. 591 if expectSignal(t, err, syscall.SIGSEGV, syscall.SIGQUIT) { 592 return 593 } 594 } 595 596 t.Errorf("program succeeded unexpectedly %d times", tries) 597 } 598 599 func TestSignalForwardingGo(t *testing.T) { 600 // This test fails on darwin-amd64 because of the special 601 // handling of user-generated SIGSEGV signals in fixsigcode in 602 // runtime/signal_darwin_amd64.go. 603 if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" { 604 t.Skip("not supported on darwin-amd64") 605 } 606 607 checkSignalForwardingTest(t) 608 buildSignalForwardingTest(t) 609 err := runSignalForwardingTest(t, "4") 610 611 // Occasionally the signal will be delivered to a C thread, 612 // and the program will crash with SIGSEGV. 613 expectSignal(t, err, syscall.SIGQUIT, syscall.SIGSEGV) 614 } 615 616 // checkSignalForwardingTest calls t.Skip if the SignalForwarding test 617 // doesn't work on this platform. 618 func checkSignalForwardingTest(t *testing.T) { 619 switch GOOS { 620 case "darwin", "ios": 621 switch GOARCH { 622 case "arm64": 623 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH) 624 } 625 case "windows": 626 t.Skip("skipping signal test on Windows") 627 } 628 } 629 630 // buildSignalForwardingTest builds the executable used by the various 631 // signal forwarding tests. 632 func buildSignalForwardingTest(t *testing.T) { 633 if !testWork { 634 t.Cleanup(func() { 635 os.Remove("libgo2.a") 636 os.Remove("libgo2.h") 637 os.Remove("testp" + exeSuffix) 638 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 639 }) 640 } 641 642 t.Log("go build -buildmode=c-archive -o libgo2.a ./libgo2") 643 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2") 644 out, err := cmd.CombinedOutput() 645 if len(out) > 0 { 646 t.Logf("%s", out) 647 } 648 if err != nil { 649 t.Fatal(err) 650 } 651 652 checkLineComments(t, "libgo2.h") 653 checkArchive(t, "libgo2.a") 654 655 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a") 656 if runtime.Compiler == "gccgo" { 657 ccArgs = append(ccArgs, "-lgo") 658 } 659 t.Log(ccArgs) 660 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() 661 if len(out) > 0 { 662 t.Logf("%s", out) 663 } 664 if err != nil { 665 t.Fatal(err) 666 } 667 } 668 669 func runSignalForwardingTest(t *testing.T, arg string) error { 670 t.Logf("%v %s", bin, arg) 671 cmd := exec.Command(bin[0], append(bin[1:], arg)...) 672 673 var out strings.Builder 674 cmd.Stdout = &out 675 676 stderr, err := cmd.StderrPipe() 677 if err != nil { 678 t.Fatal(err) 679 } 680 defer stderr.Close() 681 682 r := bufio.NewReader(stderr) 683 684 err = cmd.Start() 685 if err != nil { 686 t.Fatal(err) 687 } 688 689 // Wait for trigger to ensure that process is started. 690 ok, err := r.ReadString('\n') 691 692 // Verify trigger. 693 if err != nil || ok != "OK\n" { 694 t.Fatal("Did not receive OK signal") 695 } 696 697 var wg sync.WaitGroup 698 wg.Add(1) 699 var errsb strings.Builder 700 go func() { 701 defer wg.Done() 702 io.Copy(&errsb, r) 703 }() 704 705 // Give the program a chance to enter the function. 706 // If the program doesn't get there the test will still 707 // pass, although it doesn't quite test what we intended. 708 // This is fine as long as the program normally makes it. 709 time.Sleep(time.Millisecond) 710 711 cmd.Process.Signal(syscall.SIGSEGV) 712 713 err = cmd.Wait() 714 715 s := out.String() 716 if len(s) > 0 { 717 t.Log(s) 718 } 719 wg.Wait() 720 s = errsb.String() 721 if len(s) > 0 { 722 t.Log(s) 723 } 724 725 return err 726 } 727 728 // expectSignal checks that err, the exit status of a test program, 729 // shows a failure due to a specific signal or two. Returns whether we 730 // found an expected signal. 731 func expectSignal(t *testing.T, err error, sig1, sig2 syscall.Signal) bool { 732 t.Helper() 733 if err == nil { 734 t.Error("test program succeeded unexpectedly") 735 } else if ee, ok := err.(*exec.ExitError); !ok { 736 t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err) 737 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok { 738 t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys()) 739 } else if !ws.Signaled() || (ws.Signal() != sig1 && ws.Signal() != sig2) { 740 if sig2 == 0 { 741 t.Errorf("got %q; expected signal %q", ee, sig1) 742 } else { 743 t.Errorf("got %q; expected signal %q or %q", ee, sig1, sig2) 744 } 745 } else { 746 return true 747 } 748 return false 749 } 750 751 func TestOsSignal(t *testing.T) { 752 switch GOOS { 753 case "windows": 754 t.Skip("skipping signal test on Windows") 755 } 756 757 if !testWork { 758 defer func() { 759 os.Remove("libgo3.a") 760 os.Remove("libgo3.h") 761 os.Remove("testp" + exeSuffix) 762 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 763 }() 764 } 765 766 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "./libgo3") 767 if out, err := cmd.CombinedOutput(); err != nil { 768 t.Logf("%s", out) 769 t.Fatal(err) 770 } 771 checkLineComments(t, "libgo3.h") 772 checkArchive(t, "libgo3.a") 773 774 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a") 775 if runtime.Compiler == "gccgo" { 776 ccArgs = append(ccArgs, "-lgo") 777 } 778 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 779 t.Logf("%s", out) 780 t.Fatal(err) 781 } 782 783 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil { 784 t.Logf("%s", out) 785 t.Fatal(err) 786 } 787 } 788 789 func TestSigaltstack(t *testing.T) { 790 switch GOOS { 791 case "windows": 792 t.Skip("skipping signal test on Windows") 793 } 794 795 if !testWork { 796 defer func() { 797 os.Remove("libgo4.a") 798 os.Remove("libgo4.h") 799 os.Remove("testp" + exeSuffix) 800 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 801 }() 802 } 803 804 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "./libgo4") 805 if out, err := cmd.CombinedOutput(); err != nil { 806 t.Logf("%s", out) 807 t.Fatal(err) 808 } 809 checkLineComments(t, "libgo4.h") 810 checkArchive(t, "libgo4.a") 811 812 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a") 813 if runtime.Compiler == "gccgo" { 814 ccArgs = append(ccArgs, "-lgo") 815 } 816 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 817 t.Logf("%s", out) 818 t.Fatal(err) 819 } 820 821 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil { 822 t.Logf("%s", out) 823 t.Fatal(err) 824 } 825 } 826 827 const testar = `#!/usr/bin/env bash 828 while [[ $1 == -* ]] >/dev/null; do 829 shift 830 done 831 echo "testar" > $1 832 echo "testar" > PWD/testar.ran 833 ` 834 835 func TestExtar(t *testing.T) { 836 switch GOOS { 837 case "windows": 838 t.Skip("skipping signal test on Windows") 839 } 840 if runtime.Compiler == "gccgo" { 841 t.Skip("skipping -extar test when using gccgo") 842 } 843 if runtime.GOOS == "ios" { 844 t.Skip("shell scripts are not executable on iOS hosts") 845 } 846 847 if !testWork { 848 defer func() { 849 os.Remove("libgo4.a") 850 os.Remove("libgo4.h") 851 os.Remove("testar") 852 os.Remove("testar.ran") 853 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 854 }() 855 } 856 857 os.Remove("testar") 858 dir, err := os.Getwd() 859 if err != nil { 860 t.Fatal(err) 861 } 862 s := strings.Replace(testar, "PWD", dir, 1) 863 if err := os.WriteFile("testar", []byte(s), 0777); err != nil { 864 t.Fatal(err) 865 } 866 867 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "./libgo4") 868 if out, err := cmd.CombinedOutput(); err != nil { 869 t.Logf("%s", out) 870 t.Fatal(err) 871 } 872 checkLineComments(t, "libgo4.h") 873 874 if _, err := os.Stat("testar.ran"); err != nil { 875 if os.IsNotExist(err) { 876 t.Error("testar does not exist after go build") 877 } else { 878 t.Errorf("error checking testar: %v", err) 879 } 880 } 881 } 882 883 func TestPIE(t *testing.T) { 884 switch GOOS { 885 case "windows", "darwin", "ios", "plan9": 886 t.Skipf("skipping PIE test on %s", GOOS) 887 } 888 889 libgoa := "libgo.a" 890 if runtime.Compiler == "gccgo" { 891 libgoa = "liblibgo.a" 892 } 893 894 if !testWork { 895 defer func() { 896 os.Remove("testp" + exeSuffix) 897 os.Remove(libgoa) 898 os.RemoveAll(filepath.Join(GOPATH, "pkg")) 899 }() 900 } 901 902 // Generate the p.h header file. 903 // 904 // 'go install -i -buildmode=c-archive ./libgo' would do that too, but that 905 // would also attempt to install transitive standard-library dependencies to 906 // GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may 907 // be running this test in a GOROOT owned by root.) 908 genHeader(t, "p.h", "./p") 909 910 cmd := exec.Command("go", "build", "-buildmode=c-archive", "./libgo") 911 if out, err := cmd.CombinedOutput(); err != nil { 912 t.Logf("%s", out) 913 t.Fatal(err) 914 } 915 916 ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", libgoa) 917 if runtime.Compiler == "gccgo" { 918 ccArgs = append(ccArgs, "-lgo") 919 } 920 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil { 921 t.Logf("%s", out) 922 t.Fatal(err) 923 } 924 925 binArgs := append(bin, "arg1", "arg2") 926 cmd = exec.Command(binArgs[0], binArgs[1:]...) 927 if runtime.Compiler == "gccgo" { 928 cmd.Env = append(os.Environ(), "GCCGO=1") 929 } 930 if out, err := cmd.CombinedOutput(); err != nil { 931 t.Logf("%s", out) 932 t.Fatal(err) 933 } 934 935 if GOOS != "aix" { 936 f, err := elf.Open("testp" + exeSuffix) 937 if err != nil { 938 t.Fatal("elf.Open failed: ", err) 939 } 940 defer f.Close() 941 if hasDynTag(t, f, elf.DT_TEXTREL) { 942 t.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix) 943 } 944 } 945 } 946 947 func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool { 948 ds := f.SectionByType(elf.SHT_DYNAMIC) 949 if ds == nil { 950 t.Error("no SHT_DYNAMIC section") 951 return false 952 } 953 d, err := ds.Data() 954 if err != nil { 955 t.Errorf("can't read SHT_DYNAMIC contents: %v", err) 956 return false 957 } 958 for len(d) > 0 { 959 var t elf.DynTag 960 switch f.Class { 961 case elf.ELFCLASS32: 962 t = elf.DynTag(f.ByteOrder.Uint32(d[:4])) 963 d = d[8:] 964 case elf.ELFCLASS64: 965 t = elf.DynTag(f.ByteOrder.Uint64(d[:8])) 966 d = d[16:] 967 } 968 if t == tag { 969 return true 970 } 971 } 972 return false 973 } 974 975 func TestSIGPROF(t *testing.T) { 976 switch GOOS { 977 case "windows", "plan9": 978 t.Skipf("skipping SIGPROF test on %s", GOOS) 979 case "darwin", "ios": 980 t.Skipf("skipping SIGPROF test on %s; see https://golang.org/issue/19320", GOOS) 981 } 982 983 t.Parallel() 984 985 if !testWork { 986 defer func() { 987 os.Remove("testp6" + exeSuffix) 988 os.Remove("libgo6.a") 989 os.Remove("libgo6.h") 990 }() 991 } 992 993 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6") 994 out, err := cmd.CombinedOutput() 995 t.Logf("%v\n%s", cmd.Args, out) 996 if err != nil { 997 t.Fatal(err) 998 } 999 checkLineComments(t, "libgo6.h") 1000 checkArchive(t, "libgo6.a") 1001 1002 ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a") 1003 if runtime.Compiler == "gccgo" { 1004 ccArgs = append(ccArgs, "-lgo") 1005 } 1006 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() 1007 t.Logf("%v\n%s", ccArgs, out) 1008 if err != nil { 1009 t.Fatal(err) 1010 } 1011 1012 argv := cmdToRun("./testp6") 1013 cmd = exec.Command(argv[0], argv[1:]...) 1014 out, err = cmd.CombinedOutput() 1015 t.Logf("%v\n%s", argv, out) 1016 if err != nil { 1017 t.Fatal(err) 1018 } 1019 } 1020 1021 // TestCompileWithoutShared tests that if we compile code without the 1022 // -shared option, we can put it into an archive. When we use the go 1023 // tool with -buildmode=c-archive, it passes -shared to the compiler, 1024 // so we override that. The go tool doesn't work this way, but Bazel 1025 // will likely do it in the future. And it ought to work. This test 1026 // was added because at one time it did not work on PPC Linux. 1027 func TestCompileWithoutShared(t *testing.T) { 1028 // For simplicity, reuse the signal forwarding test. 1029 checkSignalForwardingTest(t) 1030 1031 if !testWork { 1032 defer func() { 1033 os.Remove("libgo2.a") 1034 os.Remove("libgo2.h") 1035 }() 1036 } 1037 1038 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2") 1039 out, err := cmd.CombinedOutput() 1040 t.Logf("%v\n%s", cmd.Args, out) 1041 if err != nil { 1042 t.Fatal(err) 1043 } 1044 checkLineComments(t, "libgo2.h") 1045 checkArchive(t, "libgo2.a") 1046 1047 exe := "./testnoshared" + exeSuffix 1048 1049 // In some cases, -no-pie is needed here, but not accepted everywhere. First try 1050 // if -no-pie is accepted. See #22126. 1051 ccArgs := append(cc, "-o", exe, "-no-pie", "main5.c", "libgo2.a") 1052 if runtime.Compiler == "gccgo" { 1053 ccArgs = append(ccArgs, "-lgo") 1054 } 1055 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() 1056 t.Logf("%v\n%s", ccArgs, out) 1057 1058 // If -no-pie unrecognized, try -nopie if this is possibly clang 1059 if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") { 1060 ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a") 1061 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() 1062 t.Logf("%v\n%s", ccArgs, out) 1063 } 1064 1065 // Don't use either -no-pie or -nopie 1066 if err != nil && bytes.Contains(out, []byte("unrecognized")) { 1067 ccArgs = append(cc, "-o", exe, "main5.c", "libgo2.a") 1068 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() 1069 t.Logf("%v\n%s", ccArgs, out) 1070 } 1071 if err != nil { 1072 t.Fatal(err) 1073 } 1074 if !testWork { 1075 defer os.Remove(exe) 1076 } 1077 1078 binArgs := append(cmdToRun(exe), "1") 1079 out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() 1080 t.Logf("%v\n%s", binArgs, out) 1081 expectSignal(t, err, syscall.SIGSEGV, 0) 1082 1083 // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384. 1084 if runtime.GOOS != "darwin" && runtime.GOOS != "ios" { 1085 binArgs := append(cmdToRun(exe), "3") 1086 out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput() 1087 t.Logf("%v\n%s", binArgs, out) 1088 expectSignal(t, err, syscall.SIGPIPE, 0) 1089 } 1090 } 1091 1092 // Test that installing a second time recreates the header file. 1093 func TestCachedInstall(t *testing.T) { 1094 if !testWork { 1095 defer os.RemoveAll(filepath.Join(GOPATH, "pkg")) 1096 } 1097 1098 h := filepath.Join(libgodir, "libgo.h") 1099 1100 buildcmd := []string{"go", "install", "-buildmode=c-archive", "./libgo"} 1101 1102 cmd := exec.Command(buildcmd[0], buildcmd[1:]...) 1103 cmd.Env = append(cmd.Environ(), "GO111MODULE=off") // 'go install' only works in GOPATH mode 1104 t.Log(buildcmd) 1105 if out, err := cmd.CombinedOutput(); err != nil { 1106 t.Logf("%s", out) 1107 t.Fatal(err) 1108 } 1109 1110 if _, err := os.Stat(h); err != nil { 1111 t.Errorf("libgo.h not installed: %v", err) 1112 } 1113 1114 if err := os.Remove(h); err != nil { 1115 t.Fatal(err) 1116 } 1117 1118 cmd = exec.Command(buildcmd[0], buildcmd[1:]...) 1119 cmd.Env = append(cmd.Environ(), "GO111MODULE=off") 1120 t.Log(buildcmd) 1121 if out, err := cmd.CombinedOutput(); err != nil { 1122 t.Logf("%s", out) 1123 t.Fatal(err) 1124 } 1125 1126 if _, err := os.Stat(h); err != nil { 1127 t.Errorf("libgo.h not installed in second run: %v", err) 1128 } 1129 } 1130 1131 // Issue 35294. 1132 func TestManyCalls(t *testing.T) { 1133 t.Parallel() 1134 1135 if !testWork { 1136 defer func() { 1137 os.Remove("testp7" + exeSuffix) 1138 os.Remove("libgo7.a") 1139 os.Remove("libgo7.h") 1140 }() 1141 } 1142 1143 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7") 1144 out, err := cmd.CombinedOutput() 1145 t.Logf("%v\n%s", cmd.Args, out) 1146 if err != nil { 1147 t.Fatal(err) 1148 } 1149 checkLineComments(t, "libgo7.h") 1150 checkArchive(t, "libgo7.a") 1151 1152 ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a") 1153 if runtime.Compiler == "gccgo" { 1154 ccArgs = append(ccArgs, "-lgo") 1155 } 1156 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() 1157 t.Logf("%v\n%s", ccArgs, out) 1158 if err != nil { 1159 t.Fatal(err) 1160 } 1161 1162 argv := cmdToRun("./testp7") 1163 cmd = exec.Command(argv[0], argv[1:]...) 1164 sb := new(strings.Builder) 1165 cmd.Stdout = sb 1166 cmd.Stderr = sb 1167 if err := cmd.Start(); err != nil { 1168 t.Fatal(err) 1169 } 1170 1171 timer := time.AfterFunc(time.Minute, 1172 func() { 1173 t.Error("test program timed out") 1174 cmd.Process.Kill() 1175 }, 1176 ) 1177 defer timer.Stop() 1178 1179 err = cmd.Wait() 1180 t.Logf("%v\n%s", cmd.Args, sb) 1181 if err != nil { 1182 t.Error(err) 1183 } 1184 } 1185 1186 // Issue 49288. 1187 func TestPreemption(t *testing.T) { 1188 if runtime.Compiler == "gccgo" { 1189 t.Skip("skipping asynchronous preemption test with gccgo") 1190 } 1191 1192 t.Parallel() 1193 1194 if !testWork { 1195 defer func() { 1196 os.Remove("testp8" + exeSuffix) 1197 os.Remove("libgo8.a") 1198 os.Remove("libgo8.h") 1199 }() 1200 } 1201 1202 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8") 1203 out, err := cmd.CombinedOutput() 1204 t.Logf("%v\n%s", cmd.Args, out) 1205 if err != nil { 1206 t.Fatal(err) 1207 } 1208 checkLineComments(t, "libgo8.h") 1209 checkArchive(t, "libgo8.a") 1210 1211 ccArgs := append(cc, "-o", "testp8"+exeSuffix, "main8.c", "libgo8.a") 1212 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() 1213 t.Logf("%v\n%s", ccArgs, out) 1214 if err != nil { 1215 t.Fatal(err) 1216 } 1217 1218 argv := cmdToRun("./testp8") 1219 cmd = exec.Command(argv[0], argv[1:]...) 1220 sb := new(strings.Builder) 1221 cmd.Stdout = sb 1222 cmd.Stderr = sb 1223 if err := cmd.Start(); err != nil { 1224 t.Fatal(err) 1225 } 1226 1227 timer := time.AfterFunc(time.Minute, 1228 func() { 1229 t.Error("test program timed out") 1230 cmd.Process.Kill() 1231 }, 1232 ) 1233 defer timer.Stop() 1234 1235 err = cmd.Wait() 1236 t.Logf("%v\n%s", cmd.Args, sb) 1237 if err != nil { 1238 t.Error(err) 1239 } 1240 }