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