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