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