libgo: update to Go1.10rc1
[official-gcc.git] / libgo / misc / cgo / testcarchive / carchive_test.go
blob4a8cc0f077b9aa9a63373ef6bfe25ae69b732af4
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.
5 package carchive_test
7 import (
8 "bufio"
9 "bytes"
10 "debug/elf"
11 "fmt"
12 "io/ioutil"
13 "os"
14 "os/exec"
15 "path/filepath"
16 "runtime"
17 "strings"
18 "syscall"
19 "testing"
20 "time"
21 "unicode"
24 // Program to run.
25 var bin []string
27 // C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
28 var cc []string
30 // An environment with GOPATH=$(pwd).
31 var gopathEnv []string
33 // ".exe" on Windows.
34 var exeSuffix string
36 var GOOS, GOARCH string
37 var libgodir string
39 func init() {
40 GOOS = goEnv("GOOS")
41 GOARCH = goEnv("GOARCH")
42 bin = cmdToRun("./testp")
44 ccOut := goEnv("CC")
45 cc = []string{string(ccOut)}
47 out := goEnv("GOGCCFLAGS")
48 quote := '\000'
49 start := 0
50 lastSpace := true
51 backslash := false
52 s := string(out)
53 for i, c := range s {
54 if quote == '\000' && unicode.IsSpace(c) {
55 if !lastSpace {
56 cc = append(cc, s[start:i])
57 lastSpace = true
59 } else {
60 if lastSpace {
61 start = i
62 lastSpace = false
64 if quote == '\000' && !backslash && (c == '"' || c == '\'') {
65 quote = c
66 backslash = false
67 } else if !backslash && quote == c {
68 quote = '\000'
69 } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
70 backslash = true
71 } else {
72 backslash = false
76 if !lastSpace {
77 cc = append(cc, s[start:])
80 if GOOS == "darwin" {
81 // For Darwin/ARM.
82 // TODO(crawshaw): can we do better?
83 cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
85 libgodir = GOOS + "_" + GOARCH
86 if runtime.Compiler == "gccgo" {
87 libgodir = "gccgo_" + libgodir + "_fPIC"
88 } else {
89 switch GOOS {
90 case "darwin":
91 if GOARCH == "arm" || GOARCH == "arm64" {
92 libgodir += "_shared"
94 case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
95 libgodir += "_shared"
98 cc = append(cc, "-I", filepath.Join("pkg", libgodir))
100 // Build an environment with GOPATH=$(pwd)
101 env := os.Environ()
102 var n []string
103 for _, e := range env {
104 if !strings.HasPrefix(e, "GOPATH=") {
105 n = append(n, e)
108 dir, err := os.Getwd()
109 if err != nil {
110 fmt.Fprintln(os.Stderr, err)
111 os.Exit(2)
113 n = append(n, "GOPATH="+dir)
114 gopathEnv = n
116 if GOOS == "windows" {
117 exeSuffix = ".exe"
121 func goEnv(key string) string {
122 out, err := exec.Command("go", "env", key).Output()
123 if err != nil {
124 fmt.Fprintf(os.Stderr, "go env %s failed:\n%s\n", key, err)
125 if ee, ok := err.(*exec.ExitError); ok {
126 fmt.Fprintf(os.Stderr, "%s", ee.Stderr)
128 os.Exit(2)
130 return strings.TrimSpace(string(out))
133 func cmdToRun(name string) []string {
134 execScript := "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec"
135 executor, err := exec.LookPath(execScript)
136 if err != nil {
137 return []string{name}
139 return []string{executor, name}
142 func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) {
143 t.Helper()
144 cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
145 cmd.Env = gopathEnv
146 t.Log(buildcmd)
147 if out, err := cmd.CombinedOutput(); err != nil {
148 t.Logf("%s", out)
149 t.Fatal(err)
151 defer func() {
152 os.Remove(libgoa)
153 os.Remove(libgoh)
156 ccArgs := append(cc, "-o", exe, "main.c")
157 if GOOS == "windows" {
158 ccArgs = append(ccArgs, "main_windows.c", libgoa, "-lntdll", "-lws2_32", "-lwinmm")
159 } else {
160 ccArgs = append(ccArgs, "main_unix.c", libgoa)
162 if runtime.Compiler == "gccgo" {
163 ccArgs = append(ccArgs, "-lgo")
165 t.Log(ccArgs)
166 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
167 t.Logf("%s", out)
168 t.Fatal(err)
170 defer os.Remove(exe)
172 binArgs := append(cmdToRun(exe), "arg1", "arg2")
173 cmd = exec.Command(binArgs[0], binArgs[1:]...)
174 if runtime.Compiler == "gccgo" {
175 cmd.Env = append(os.Environ(), "GCCGO=1")
177 if out, err := cmd.CombinedOutput(); err != nil {
178 t.Logf("%s", out)
179 t.Fatal(err)
183 func TestInstall(t *testing.T) {
184 defer os.RemoveAll("pkg")
186 libgoa := "libgo.a"
187 if runtime.Compiler == "gccgo" {
188 libgoa = "liblibgo.a"
191 testInstall(t, "./testp1"+exeSuffix,
192 filepath.Join("pkg", libgodir, libgoa),
193 filepath.Join("pkg", libgodir, "libgo.h"),
194 "go", "install", "-i", "-buildmode=c-archive", "libgo")
196 // Test building libgo other than installing it.
197 // Header files are now present.
198 testInstall(t, "./testp2"+exeSuffix, "libgo.a", "libgo.h",
199 "go", "build", "-buildmode=c-archive", filepath.Join("src", "libgo", "libgo.go"))
201 testInstall(t, "./testp3"+exeSuffix, "libgo.a", "libgo.h",
202 "go", "build", "-buildmode=c-archive", "-o", "libgo.a", "libgo")
205 func TestEarlySignalHandler(t *testing.T) {
206 switch GOOS {
207 case "darwin":
208 switch GOARCH {
209 case "arm", "arm64":
210 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
212 case "windows":
213 t.Skip("skipping signal test on Windows")
216 defer func() {
217 os.Remove("libgo2.a")
218 os.Remove("libgo2.h")
219 os.Remove("testp")
220 os.RemoveAll("pkg")
223 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
224 cmd.Env = gopathEnv
225 if out, err := cmd.CombinedOutput(); err != nil {
226 t.Logf("%s", out)
227 t.Fatal(err)
230 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a")
231 if runtime.Compiler == "gccgo" {
232 ccArgs = append(ccArgs, "-lgo")
234 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
235 t.Logf("%s", out)
236 t.Fatal(err)
239 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
240 t.Logf("%s", out)
241 t.Fatal(err)
245 func TestSignalForwarding(t *testing.T) {
246 checkSignalForwardingTest(t)
248 defer func() {
249 os.Remove("libgo2.a")
250 os.Remove("libgo2.h")
251 os.Remove("testp")
252 os.RemoveAll("pkg")
255 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
256 cmd.Env = gopathEnv
257 if out, err := cmd.CombinedOutput(); err != nil {
258 t.Logf("%s", out)
259 t.Fatal(err)
262 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
263 if runtime.Compiler == "gccgo" {
264 ccArgs = append(ccArgs, "-lgo")
266 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
267 t.Logf("%s", out)
268 t.Fatal(err)
271 cmd = exec.Command(bin[0], append(bin[1:], "1")...)
273 out, err := cmd.CombinedOutput()
274 t.Logf("%s", out)
275 expectSignal(t, err, syscall.SIGSEGV)
277 // Test SIGPIPE forwarding
278 cmd = exec.Command(bin[0], append(bin[1:], "3")...)
280 out, err = cmd.CombinedOutput()
281 t.Logf("%s", out)
282 expectSignal(t, err, syscall.SIGPIPE)
285 func TestSignalForwardingExternal(t *testing.T) {
286 checkSignalForwardingTest(t)
288 defer func() {
289 os.Remove("libgo2.a")
290 os.Remove("libgo2.h")
291 os.Remove("testp")
292 os.RemoveAll("pkg")
295 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
296 cmd.Env = gopathEnv
297 if out, err := cmd.CombinedOutput(); err != nil {
298 t.Logf("%s", out)
299 t.Fatal(err)
302 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
303 if runtime.Compiler == "gccgo" {
304 ccArgs = append(ccArgs, "-lgo")
306 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
307 t.Logf("%s", out)
308 t.Fatal(err)
311 // We want to send the process a signal and see if it dies.
312 // Normally the signal goes to the C thread, the Go signal
313 // handler picks it up, sees that it is running in a C thread,
314 // and the program dies. Unfortunately, occasionally the
315 // signal is delivered to a Go thread, which winds up
316 // discarding it because it was sent by another program and
317 // there is no Go handler for it. To avoid this, run the
318 // program several times in the hopes that it will eventually
319 // fail.
320 const tries = 20
321 for i := 0; i < tries; i++ {
322 cmd = exec.Command(bin[0], append(bin[1:], "2")...)
324 stderr, err := cmd.StderrPipe()
325 if err != nil {
326 t.Fatal(err)
328 defer stderr.Close()
330 r := bufio.NewReader(stderr)
332 err = cmd.Start()
334 if err != nil {
335 t.Fatal(err)
338 // Wait for trigger to ensure that the process is started.
339 ok, err := r.ReadString('\n')
341 // Verify trigger.
342 if err != nil || ok != "OK\n" {
343 t.Fatalf("Did not receive OK signal")
346 // Give the program a chance to enter the sleep function.
347 time.Sleep(time.Millisecond)
349 cmd.Process.Signal(syscall.SIGSEGV)
351 err = cmd.Wait()
353 if err == nil {
354 continue
357 if expectSignal(t, err, syscall.SIGSEGV) {
358 return
362 t.Errorf("program succeeded unexpectedly %d times", tries)
365 // checkSignalForwardingTest calls t.Skip if the SignalForwarding test
366 // doesn't work on this platform.
367 func checkSignalForwardingTest(t *testing.T) {
368 switch GOOS {
369 case "darwin":
370 switch GOARCH {
371 case "arm", "arm64":
372 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
374 case "windows":
375 t.Skip("skipping signal test on Windows")
379 // expectSignal checks that err, the exit status of a test program,
380 // shows a failure due to a specific signal. Returns whether we found
381 // the expected signal.
382 func expectSignal(t *testing.T, err error, sig syscall.Signal) bool {
383 if err == nil {
384 t.Error("test program succeeded unexpectedly")
385 } else if ee, ok := err.(*exec.ExitError); !ok {
386 t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
387 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
388 t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
389 } else if !ws.Signaled() || ws.Signal() != sig {
390 t.Errorf("got %v; expected signal %v", ee, sig)
391 } else {
392 return true
394 return false
397 func TestOsSignal(t *testing.T) {
398 switch GOOS {
399 case "windows":
400 t.Skip("skipping signal test on Windows")
403 defer func() {
404 os.Remove("libgo3.a")
405 os.Remove("libgo3.h")
406 os.Remove("testp")
407 os.RemoveAll("pkg")
410 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "libgo3")
411 cmd.Env = gopathEnv
412 if out, err := cmd.CombinedOutput(); err != nil {
413 t.Logf("%s", out)
414 t.Fatal(err)
417 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a")
418 if runtime.Compiler == "gccgo" {
419 ccArgs = append(ccArgs, "-lgo")
421 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
422 t.Logf("%s", out)
423 t.Fatal(err)
426 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
427 t.Logf("%s", out)
428 t.Fatal(err)
432 func TestSigaltstack(t *testing.T) {
433 switch GOOS {
434 case "windows":
435 t.Skip("skipping signal test on Windows")
438 defer func() {
439 os.Remove("libgo4.a")
440 os.Remove("libgo4.h")
441 os.Remove("testp")
442 os.RemoveAll("pkg")
445 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "libgo4")
446 cmd.Env = gopathEnv
447 if out, err := cmd.CombinedOutput(); err != nil {
448 t.Logf("%s", out)
449 t.Fatal(err)
452 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a")
453 if runtime.Compiler == "gccgo" {
454 ccArgs = append(ccArgs, "-lgo")
456 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
457 t.Logf("%s", out)
458 t.Fatal(err)
461 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
462 t.Logf("%s", out)
463 t.Fatal(err)
467 const testar = `#!/usr/bin/env bash
468 while expr $1 : '[-]' >/dev/null; do
469 shift
470 done
471 echo "testar" > $1
472 echo "testar" > PWD/testar.ran
475 func TestExtar(t *testing.T) {
476 switch GOOS {
477 case "windows":
478 t.Skip("skipping signal test on Windows")
480 if runtime.Compiler == "gccgo" {
481 t.Skip("skipping -extar test when using gccgo")
484 defer func() {
485 os.Remove("libgo4.a")
486 os.Remove("libgo4.h")
487 os.Remove("testar")
488 os.Remove("testar.ran")
489 os.RemoveAll("pkg")
492 os.Remove("testar")
493 dir, err := os.Getwd()
494 if err != nil {
495 t.Fatal(err)
497 s := strings.Replace(testar, "PWD", dir, 1)
498 if err := ioutil.WriteFile("testar", []byte(s), 0777); err != nil {
499 t.Fatal(err)
502 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "libgo4")
503 cmd.Env = gopathEnv
504 if out, err := cmd.CombinedOutput(); err != nil {
505 t.Logf("%s", out)
506 t.Fatal(err)
509 if _, err := os.Stat("testar.ran"); err != nil {
510 if os.IsNotExist(err) {
511 t.Error("testar does not exist after go build")
512 } else {
513 t.Errorf("error checking testar: %v", err)
518 func TestPIE(t *testing.T) {
519 switch GOOS {
520 case "windows", "darwin", "plan9":
521 t.Skipf("skipping PIE test on %s", GOOS)
524 defer func() {
525 os.Remove("testp" + exeSuffix)
526 os.RemoveAll("pkg")
529 cmd := exec.Command("go", "install", "-i", "-buildmode=c-archive", "libgo")
530 cmd.Env = gopathEnv
531 if out, err := cmd.CombinedOutput(); err != nil {
532 t.Logf("%s", out)
533 t.Fatal(err)
536 libgoa := "libgo.a"
537 if runtime.Compiler == "gccgo" {
538 libgoa = "liblibgo.a"
541 ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", filepath.Join("pkg", libgodir, libgoa))
542 if runtime.Compiler == "gccgo" {
543 ccArgs = append(ccArgs, "-lgo")
545 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
546 t.Logf("%s", out)
547 t.Fatal(err)
550 binArgs := append(bin, "arg1", "arg2")
551 cmd = exec.Command(binArgs[0], binArgs[1:]...)
552 if runtime.Compiler == "gccgo" {
553 cmd.Env = append(os.Environ(), "GCCGO=1")
555 if out, err := cmd.CombinedOutput(); err != nil {
556 t.Logf("%s", out)
557 t.Fatal(err)
560 f, err := elf.Open("testp" + exeSuffix)
561 if err != nil {
562 t.Fatal("elf.Open failed: ", err)
564 defer f.Close()
565 if hasDynTag(t, f, elf.DT_TEXTREL) {
566 t.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix)
570 func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool {
571 ds := f.SectionByType(elf.SHT_DYNAMIC)
572 if ds == nil {
573 t.Error("no SHT_DYNAMIC section")
574 return false
576 d, err := ds.Data()
577 if err != nil {
578 t.Errorf("can't read SHT_DYNAMIC contents: %v", err)
579 return false
581 for len(d) > 0 {
582 var t elf.DynTag
583 switch f.Class {
584 case elf.ELFCLASS32:
585 t = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
586 d = d[8:]
587 case elf.ELFCLASS64:
588 t = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
589 d = d[16:]
591 if t == tag {
592 return true
595 return false
598 func TestSIGPROF(t *testing.T) {
599 switch GOOS {
600 case "windows", "plan9":
601 t.Skipf("skipping SIGPROF test on %s", GOOS)
602 case "darwin":
603 t.Skipf("skipping SIGPROF test on %s; see https://golang.org/issue/19320", GOOS)
606 t.Parallel()
608 defer func() {
609 os.Remove("testp6" + exeSuffix)
610 os.Remove("libgo6.a")
611 os.Remove("libgo6.h")
614 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "libgo6")
615 cmd.Env = gopathEnv
616 if out, err := cmd.CombinedOutput(); err != nil {
617 t.Logf("%s", out)
618 t.Fatal(err)
621 ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a")
622 if runtime.Compiler == "gccgo" {
623 ccArgs = append(ccArgs, "-lgo")
625 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
626 t.Logf("%s", out)
627 t.Fatal(err)
630 argv := cmdToRun("./testp6")
631 cmd = exec.Command(argv[0], argv[1:]...)
632 if out, err := cmd.CombinedOutput(); err != nil {
633 t.Logf("%s", out)
634 t.Fatal(err)
638 // TestCompileWithoutShared tests that if we compile code without the
639 // -shared option, we can put it into an archive. When we use the go
640 // tool with -buildmode=c-archive, it passes -shared to the compiler,
641 // so we override that. The go tool doesn't work this way, but Bazel
642 // will likely do it in the future. And it ought to work. This test
643 // was added because at one time it did not work on PPC GNU/Linux.
644 func TestCompileWithoutShared(t *testing.T) {
645 // For simplicity, reuse the signal forwarding test.
646 checkSignalForwardingTest(t)
648 defer func() {
649 os.Remove("libgo2.a")
650 os.Remove("libgo2.h")
653 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "libgo2")
654 cmd.Env = gopathEnv
655 t.Log(cmd.Args)
656 out, err := cmd.CombinedOutput()
657 t.Logf("%s", out)
658 if err != nil {
659 t.Fatal(err)
662 exe := "./testnoshared" + exeSuffix
664 // In some cases, -no-pie is needed here, but not accepted everywhere. First try
665 // if -no-pie is accepted. See #22126.
666 ccArgs := append(cc, "-o", exe, "-no-pie", "main5.c", "libgo2.a")
667 if runtime.Compiler == "gccgo" {
668 ccArgs = append(ccArgs, "-lgo")
670 t.Log(ccArgs)
671 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
673 // If -no-pie unrecognized, try -nopie if this is possibly clang
674 if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") {
675 ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a")
676 t.Log(ccArgs)
677 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
680 // Don't use either -no-pie or -nopie
681 if err != nil && bytes.Contains(out, []byte("unrecognized")) {
682 ccArgs := append(cc, "-o", exe, "main5.c", "libgo2.a")
683 t.Log(ccArgs)
684 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
686 t.Logf("%s", out)
687 if err != nil {
688 t.Fatal(err)
690 defer os.Remove(exe)
692 binArgs := append(cmdToRun(exe), "3")
693 t.Log(binArgs)
694 out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
695 t.Logf("%s", out)
696 expectSignal(t, err, syscall.SIGPIPE)
699 // Test that installing a second time recreates the header files.
700 func TestCachedInstall(t *testing.T) {
701 defer os.RemoveAll("pkg")
703 h1 := filepath.Join("pkg", libgodir, "libgo.h")
704 h2 := filepath.Join("pkg", libgodir, "p.h")
706 buildcmd := []string{"go", "install", "-i", "-buildmode=c-archive", "libgo"}
708 cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
709 cmd.Env = gopathEnv
710 t.Log(buildcmd)
711 if out, err := cmd.CombinedOutput(); err != nil {
712 t.Logf("%s", out)
713 t.Fatal(err)
716 if _, err := os.Stat(h1); err != nil {
717 t.Errorf("libgo.h not installed: %v", err)
719 if _, err := os.Stat(h2); err != nil {
720 t.Errorf("p.h not installed: %v", err)
723 if err := os.Remove(h1); err != nil {
724 t.Fatal(err)
726 if err := os.Remove(h2); err != nil {
727 t.Fatal(err)
730 cmd = exec.Command(buildcmd[0], buildcmd[1:]...)
731 cmd.Env = gopathEnv
732 t.Log(buildcmd)
733 if out, err := cmd.CombinedOutput(); err != nil {
734 t.Logf("%s", out)
735 t.Fatal(err)
738 if _, err := os.Stat(h1); err != nil {
739 t.Errorf("libgo.h not installed in second run: %v", err)
741 if _, err := os.Stat(h2); err != nil {
742 t.Errorf("p.h not installed in second run: %v", err)