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