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