misc/cgo/testcarchive: fix test to work for gccgo
[official-gcc.git] / libgo / misc / cgo / testcarchive / carchive_test.go
bloba2ad9c5641897bbcfb36afd28a56ef89ac31ff67
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 "debug/elf"
10 "fmt"
11 "io/ioutil"
12 "os"
13 "os/exec"
14 "path/filepath"
15 "runtime"
16 "strings"
17 "syscall"
18 "testing"
19 "time"
20 "unicode"
23 // Program to run.
24 var bin []string
26 // C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
27 var cc []string
29 // An environment with GOPATH=$(pwd).
30 var gopathEnv []string
32 // ".exe" on Windows.
33 var exeSuffix string
35 var GOOS, GOARCH string
36 var libgodir string
38 func init() {
39 GOOS = goEnv("GOOS")
40 GOARCH = goEnv("GOARCH")
41 bin = cmdToRun("./testp")
43 ccOut := goEnv("CC")
44 cc = []string{string(ccOut)}
46 out := goEnv("GOGCCFLAGS")
47 quote := '\000'
48 start := 0
49 lastSpace := true
50 backslash := false
51 s := string(out)
52 for i, c := range s {
53 if quote == '\000' && unicode.IsSpace(c) {
54 if !lastSpace {
55 cc = append(cc, s[start:i])
56 lastSpace = true
58 } else {
59 if lastSpace {
60 start = i
61 lastSpace = false
63 if quote == '\000' && !backslash && (c == '"' || c == '\'') {
64 quote = c
65 backslash = false
66 } else if !backslash && quote == c {
67 quote = '\000'
68 } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
69 backslash = true
70 } else {
71 backslash = false
75 if !lastSpace {
76 cc = append(cc, s[start:])
79 if GOOS == "darwin" {
80 // For Darwin/ARM.
81 // TODO(crawshaw): can we do better?
82 cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
84 libgodir = GOOS + "_" + GOARCH
85 if runtime.Compiler == "gccgo" {
86 libgodir = "gccgo_" + libgodir + "_fPIC"
87 } else {
88 switch GOOS {
89 case "darwin":
90 if GOARCH == "arm" || GOARCH == "arm64" {
91 libgodir += "_shared"
93 case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
94 libgodir += "_shared"
97 cc = append(cc, "-I", filepath.Join("pkg", libgodir))
99 // Build an environment with GOPATH=$(pwd)
100 env := os.Environ()
101 var n []string
102 for _, e := range env {
103 if !strings.HasPrefix(e, "GOPATH=") {
104 n = append(n, e)
107 dir, err := os.Getwd()
108 if err != nil {
109 fmt.Fprintln(os.Stderr, err)
110 os.Exit(2)
112 n = append(n, "GOPATH="+dir)
113 gopathEnv = n
115 if GOOS == "windows" {
116 exeSuffix = ".exe"
120 func goEnv(key string) string {
121 out, err := exec.Command("go", "env", key).Output()
122 if err != nil {
123 fmt.Fprintf(os.Stderr, "go env %s failed:\n%s", key, err)
124 fmt.Fprintf(os.Stderr, "%s", err.(*exec.ExitError).Stderr)
125 os.Exit(2)
127 return strings.TrimSpace(string(out))
130 func cmdToRun(name string) []string {
131 execScript := "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec"
132 executor, err := exec.LookPath(execScript)
133 if err != nil {
134 return []string{name}
136 return []string{executor, name}
139 func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) {
140 cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
141 cmd.Env = gopathEnv
142 if out, err := cmd.CombinedOutput(); err != nil {
143 t.Logf("%s", out)
144 t.Fatal(err)
146 defer func() {
147 os.Remove(libgoa)
148 os.Remove(libgoh)
151 ccArgs := append(cc, "-o", exe, "main.c")
152 if GOOS == "windows" {
153 ccArgs = append(ccArgs, "main_windows.c", libgoa, "-lntdll", "-lws2_32", "-lwinmm")
154 } else {
155 ccArgs = append(ccArgs, "main_unix.c", libgoa)
157 if runtime.Compiler == "gccgo" {
158 ccArgs = append(ccArgs, "-lgo")
160 t.Log(ccArgs)
161 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
162 t.Logf("%s", out)
163 t.Fatal(err)
165 defer os.Remove(exe)
167 binArgs := append(cmdToRun(exe), "arg1", "arg2")
168 cmd = exec.Command(binArgs[0], binArgs[1:]...)
169 if runtime.Compiler == "gccgo" {
170 cmd.Env = append(os.Environ(), "GCCGO=1")
172 if out, err := cmd.CombinedOutput(); err != nil {
173 t.Logf("%s", out)
174 t.Fatal(err)
178 func TestInstall(t *testing.T) {
179 defer os.RemoveAll("pkg")
181 libgoa := "libgo.a"
182 if runtime.Compiler == "gccgo" {
183 libgoa = "liblibgo.a"
186 testInstall(t, "./testp1"+exeSuffix,
187 filepath.Join("pkg", libgodir, libgoa),
188 filepath.Join("pkg", libgodir, "libgo.h"),
189 "go", "install", "-buildmode=c-archive", "libgo")
191 // Test building libgo other than installing it.
192 // Header files are now present.
193 testInstall(t, "./testp2"+exeSuffix, "libgo.a", "libgo.h",
194 "go", "build", "-buildmode=c-archive", filepath.Join("src", "libgo", "libgo.go"))
196 testInstall(t, "./testp3"+exeSuffix, "libgo.a", "libgo.h",
197 "go", "build", "-buildmode=c-archive", "-o", "libgo.a", "libgo")
200 func TestEarlySignalHandler(t *testing.T) {
201 switch GOOS {
202 case "darwin":
203 switch GOARCH {
204 case "arm", "arm64":
205 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
207 case "windows":
208 t.Skip("skipping signal test on Windows")
211 defer func() {
212 os.Remove("libgo2.a")
213 os.Remove("libgo2.h")
214 os.Remove("testp")
215 os.RemoveAll("pkg")
218 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
219 cmd.Env = gopathEnv
220 if out, err := cmd.CombinedOutput(); err != nil {
221 t.Logf("%s", out)
222 t.Fatal(err)
225 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a")
226 if runtime.Compiler == "gccgo" {
227 ccArgs = append(ccArgs, "-lgo")
229 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
230 t.Logf("%s", out)
231 t.Fatal(err)
234 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
235 t.Logf("%s", out)
236 t.Fatal(err)
240 func TestSignalForwarding(t *testing.T) {
241 switch GOOS {
242 case "darwin":
243 switch GOARCH {
244 case "arm", "arm64":
245 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
247 case "windows":
248 t.Skip("skipping signal test on Windows")
251 defer func() {
252 os.Remove("libgo2.a")
253 os.Remove("libgo2.h")
254 os.Remove("testp")
255 os.RemoveAll("pkg")
258 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
259 cmd.Env = gopathEnv
260 if out, err := cmd.CombinedOutput(); err != nil {
261 t.Logf("%s", out)
262 t.Fatal(err)
265 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
266 if runtime.Compiler == "gccgo" {
267 ccArgs = append(ccArgs, "-lgo")
269 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
270 t.Logf("%s", out)
271 t.Fatal(err)
274 cmd = exec.Command(bin[0], append(bin[1:], "1")...)
276 out, err := cmd.CombinedOutput()
278 if err == nil {
279 t.Logf("%s", out)
280 t.Error("test program succeeded unexpectedly")
281 } else if ee, ok := err.(*exec.ExitError); !ok {
282 t.Logf("%s", out)
283 t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
284 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
285 t.Logf("%s", out)
286 t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
287 } else if !ws.Signaled() || ws.Signal() != syscall.SIGSEGV {
288 t.Logf("%s", out)
289 t.Errorf("got %v; expected SIGSEGV", ee)
293 func TestSignalForwardingExternal(t *testing.T) {
294 switch GOOS {
295 case "darwin":
296 switch GOARCH {
297 case "arm", "arm64":
298 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
300 case "windows":
301 t.Skip("skipping signal test on Windows")
304 defer func() {
305 os.Remove("libgo2.a")
306 os.Remove("libgo2.h")
307 os.Remove("testp")
308 os.RemoveAll("pkg")
311 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "libgo2")
312 cmd.Env = gopathEnv
313 if out, err := cmd.CombinedOutput(); err != nil {
314 t.Logf("%s", out)
315 t.Fatal(err)
318 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
319 if runtime.Compiler == "gccgo" {
320 ccArgs = append(ccArgs, "-lgo")
322 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
323 t.Logf("%s", out)
324 t.Fatal(err)
327 // We want to send the process a signal and see if it dies.
328 // Normally the signal goes to the C thread, the Go signal
329 // handler picks it up, sees that it is running in a C thread,
330 // and the program dies. Unfortunately, occasionally the
331 // signal is delivered to a Go thread, which winds up
332 // discarding it because it was sent by another program and
333 // there is no Go handler for it. To avoid this, run the
334 // program several times in the hopes that it will eventually
335 // fail.
336 const tries = 20
337 for i := 0; i < tries; i++ {
338 cmd = exec.Command(bin[0], append(bin[1:], "2")...)
340 stderr, err := cmd.StderrPipe()
341 if err != nil {
342 t.Fatal(err)
344 defer stderr.Close()
346 r := bufio.NewReader(stderr)
348 err = cmd.Start()
350 if err != nil {
351 t.Fatal(err)
354 // Wait for trigger to ensure that the process is started.
355 ok, err := r.ReadString('\n')
357 // Verify trigger.
358 if err != nil || ok != "OK\n" {
359 t.Fatalf("Did not receive OK signal")
362 // Give the program a chance to enter the sleep function.
363 time.Sleep(time.Millisecond)
365 cmd.Process.Signal(syscall.SIGSEGV)
367 err = cmd.Wait()
369 if err == nil {
370 continue
373 if ee, ok := err.(*exec.ExitError); !ok {
374 t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
375 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
376 t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
377 } else if !ws.Signaled() || ws.Signal() != syscall.SIGSEGV {
378 t.Errorf("got %v; expected SIGSEGV", ee)
379 } else {
380 // We got the error we expected.
381 return
385 t.Errorf("program succeeded unexpectedly %d times", tries)
388 func TestOsSignal(t *testing.T) {
389 switch GOOS {
390 case "windows":
391 t.Skip("skipping signal test on Windows")
394 defer func() {
395 os.Remove("libgo3.a")
396 os.Remove("libgo3.h")
397 os.Remove("testp")
398 os.RemoveAll("pkg")
401 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "libgo3")
402 cmd.Env = gopathEnv
403 if out, err := cmd.CombinedOutput(); err != nil {
404 t.Logf("%s", out)
405 t.Fatal(err)
408 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a")
409 if runtime.Compiler == "gccgo" {
410 ccArgs = append(ccArgs, "-lgo")
412 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
413 t.Logf("%s", out)
414 t.Fatal(err)
417 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
418 t.Logf("%s", out)
419 t.Fatal(err)
423 func TestSigaltstack(t *testing.T) {
424 switch GOOS {
425 case "windows":
426 t.Skip("skipping signal test on Windows")
429 defer func() {
430 os.Remove("libgo4.a")
431 os.Remove("libgo4.h")
432 os.Remove("testp")
433 os.RemoveAll("pkg")
436 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "libgo4")
437 cmd.Env = gopathEnv
438 if out, err := cmd.CombinedOutput(); err != nil {
439 t.Logf("%s", out)
440 t.Fatal(err)
443 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a")
444 if runtime.Compiler == "gccgo" {
445 ccArgs = append(ccArgs, "-lgo")
447 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
448 t.Logf("%s", out)
449 t.Fatal(err)
452 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
453 t.Logf("%s", out)
454 t.Fatal(err)
458 const testar = `#!/usr/bin/env bash
459 while expr $1 : '[-]' >/dev/null; do
460 shift
461 done
462 echo "testar" > $1
463 echo "testar" > PWD/testar.ran
466 func TestExtar(t *testing.T) {
467 switch GOOS {
468 case "windows":
469 t.Skip("skipping signal test on Windows")
471 if runtime.Compiler == "gccgo" {
472 t.Skip("skipping -extar test when using gccgo")
475 defer func() {
476 os.Remove("libgo4.a")
477 os.Remove("libgo4.h")
478 os.Remove("testar")
479 os.Remove("testar.ran")
480 os.RemoveAll("pkg")
483 os.Remove("testar")
484 dir, err := os.Getwd()
485 if err != nil {
486 t.Fatal(err)
488 s := strings.Replace(testar, "PWD", dir, 1)
489 if err := ioutil.WriteFile("testar", []byte(s), 0777); err != nil {
490 t.Fatal(err)
493 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "libgo4")
494 cmd.Env = gopathEnv
495 if out, err := cmd.CombinedOutput(); err != nil {
496 t.Logf("%s", out)
497 t.Fatal(err)
500 if _, err := os.Stat("testar.ran"); err != nil {
501 if os.IsNotExist(err) {
502 t.Error("testar does not exist after go build")
503 } else {
504 t.Errorf("error checking testar: %v", err)
509 func TestPIE(t *testing.T) {
510 switch GOOS {
511 case "windows", "darwin", "plan9":
512 t.Skipf("skipping PIE test on %s", GOOS)
515 defer func() {
516 os.Remove("testp" + exeSuffix)
517 os.RemoveAll("pkg")
520 cmd := exec.Command("go", "install", "-buildmode=c-archive", "libgo")
521 cmd.Env = gopathEnv
522 if out, err := cmd.CombinedOutput(); err != nil {
523 t.Logf("%s", out)
524 t.Fatal(err)
527 libgoa := "libgo.a"
528 if runtime.Compiler == "gccgo" {
529 libgoa = "liblibgo.a"
532 ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", filepath.Join("pkg", libgodir, libgoa))
533 if runtime.Compiler == "gccgo" {
534 ccArgs = append(ccArgs, "-lgo")
536 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
537 t.Logf("%s", out)
538 t.Fatal(err)
541 binArgs := append(bin, "arg1", "arg2")
542 cmd = exec.Command(binArgs[0], binArgs[1:]...)
543 if runtime.Compiler == "gccgo" {
544 cmd.Env = append(os.Environ(), "GCCGO=1")
546 if out, err := cmd.CombinedOutput(); err != nil {
547 t.Logf("%s", out)
548 t.Fatal(err)
551 f, err := elf.Open("testp" + exeSuffix)
552 if err != nil {
553 t.Fatal("elf.Open failed: ", err)
555 defer f.Close()
556 if hasDynTag(t, f, elf.DT_TEXTREL) {
557 t.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix)
561 func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool {
562 ds := f.SectionByType(elf.SHT_DYNAMIC)
563 if ds == nil {
564 t.Error("no SHT_DYNAMIC section")
565 return false
567 d, err := ds.Data()
568 if err != nil {
569 t.Errorf("can't read SHT_DYNAMIC contents: %v", err)
570 return false
572 for len(d) > 0 {
573 var t elf.DynTag
574 switch f.Class {
575 case elf.ELFCLASS32:
576 t = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
577 d = d[8:]
578 case elf.ELFCLASS64:
579 t = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
580 d = d[16:]
582 if t == tag {
583 return true
586 return false