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