1
2
3
4
5
6
7
8
9
10 package carchive_test
11
12 import (
13 "bufio"
14 "bytes"
15 "cmd/cgo/internal/cgotest"
16 "debug/elf"
17 "flag"
18 "fmt"
19 "internal/testenv"
20 "io"
21 "log"
22 "os"
23 "os/exec"
24 "path/filepath"
25 "regexp"
26 "runtime"
27 "strconv"
28 "strings"
29 "sync"
30 "syscall"
31 "testing"
32 "time"
33 "unicode"
34 )
35
36 var globalSkip = func(t testing.TB) {}
37
38
39 var bin []string
40
41
42 var cc []string
43
44
45 var exeSuffix string
46
47 var GOOS, GOARCH, GOPATH string
48 var libgodir string
49
50 var testWork bool
51
52 func TestMain(m *testing.M) {
53 flag.BoolVar(&testWork, "testwork", false, "if true, log and preserve the test's temporary working directory")
54 flag.Parse()
55
56 log.SetFlags(log.Lshortfile)
57 os.Exit(testMain(m))
58 }
59
60 func testMain(m *testing.M) int {
61 if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
62 globalSkip = func(t testing.TB) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
63 return m.Run()
64 }
65 if runtime.GOOS == "linux" {
66 if _, err := os.Stat("/etc/alpine-release"); err == nil {
67 globalSkip = func(t testing.TB) { t.Skip("skipping failing test on alpine - go.dev/issue/19938") }
68 return m.Run()
69 }
70 }
71
72
73
74 var err error
75 GOPATH, err = os.MkdirTemp("", "carchive_test")
76 if err != nil {
77 log.Panic(err)
78 }
79 if testWork {
80 log.Println(GOPATH)
81 } else {
82 defer os.RemoveAll(GOPATH)
83 }
84 os.Setenv("GOPATH", GOPATH)
85
86
87
88 modRoot := filepath.Join(GOPATH, "src", "testcarchive")
89 if err := cgotest.OverlayDir(modRoot, "testdata"); err != nil {
90 log.Panic(err)
91 }
92 if err := os.Chdir(modRoot); err != nil {
93 log.Panic(err)
94 }
95 os.Setenv("PWD", modRoot)
96 if err := os.WriteFile("go.mod", []byte("module testcarchive\n"), 0666); err != nil {
97 log.Panic(err)
98 }
99
100 GOOS = goEnv("GOOS")
101 GOARCH = goEnv("GOARCH")
102 bin = cmdToRun("./testp")
103
104 ccOut := goEnv("CC")
105 cc = []string{string(ccOut)}
106
107 out := goEnv("GOGCCFLAGS")
108 quote := '\000'
109 start := 0
110 lastSpace := true
111 backslash := false
112 s := string(out)
113 for i, c := range s {
114 if quote == '\000' && unicode.IsSpace(c) {
115 if !lastSpace {
116 cc = append(cc, s[start:i])
117 lastSpace = true
118 }
119 } else {
120 if lastSpace {
121 start = i
122 lastSpace = false
123 }
124 if quote == '\000' && !backslash && (c == '"' || c == '\'') {
125 quote = c
126 backslash = false
127 } else if !backslash && quote == c {
128 quote = '\000'
129 } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
130 backslash = true
131 } else {
132 backslash = false
133 }
134 }
135 }
136 if !lastSpace {
137 cc = append(cc, s[start:])
138 }
139
140 if GOOS == "aix" {
141
142
143 cc = append(cc, "-Wl,-bnoobjreorder")
144 }
145 if GOOS == "ios" {
146
147
148
149
150
151
152 cc = append(cc, "-framework", "CoreFoundation")
153 }
154 libbase := GOOS + "_" + GOARCH
155 if runtime.Compiler == "gccgo" {
156 libbase = "gccgo_" + libgodir + "_fPIC"
157 } else {
158 switch GOOS {
159 case "darwin", "ios":
160 if GOARCH == "arm64" {
161 libbase += "_shared"
162 }
163 case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris", "illumos":
164 libbase += "_shared"
165 }
166 }
167 libgodir = filepath.Join(GOPATH, "pkg", libbase, "testcarchive")
168 cc = append(cc, "-I", libgodir)
169
170
171 cc = cc[:len(cc):len(cc)]
172
173 if GOOS == "windows" {
174 exeSuffix = ".exe"
175 }
176
177 return m.Run()
178 }
179
180 func goEnv(key string) string {
181 out, err := exec.Command("go", "env", key).Output()
182 if err != nil {
183 if ee, ok := err.(*exec.ExitError); ok {
184 fmt.Fprintf(os.Stderr, "%s", ee.Stderr)
185 }
186 log.Panicf("go env %s failed:\n%s\n", key, err)
187 }
188 return strings.TrimSpace(string(out))
189 }
190
191 func cmdToRun(name string) []string {
192 execScript := "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec"
193 executor, err := exec.LookPath(execScript)
194 if err != nil {
195 return []string{name}
196 }
197 return []string{executor, name}
198 }
199
200
201
202
203
204 func genHeader(t *testing.T, header, dir string) {
205 t.Helper()
206
207
208
209
210 objDir, err := os.MkdirTemp(GOPATH, "_obj")
211 if err != nil {
212 t.Fatal(err)
213 }
214 defer os.RemoveAll(objDir)
215
216 files, err := filepath.Glob(filepath.Join(dir, "*.go"))
217 if err != nil {
218 t.Fatal(err)
219 }
220
221 cmd := exec.Command("go", "tool", "cgo",
222 "-objdir", objDir,
223 "-exportheader", header)
224 cmd.Args = append(cmd.Args, files...)
225 t.Log(cmd.Args)
226 if out, err := cmd.CombinedOutput(); err != nil {
227 t.Logf("%s", out)
228 t.Fatal(err)
229 }
230 }
231
232 func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) {
233 t.Helper()
234 cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
235 cmd.Env = append(cmd.Environ(), "GO111MODULE=off")
236 t.Log(buildcmd)
237 if out, err := cmd.CombinedOutput(); err != nil {
238 t.Logf("%s", out)
239 t.Fatal(err)
240 }
241 if !testWork {
242 defer func() {
243 os.Remove(libgoa)
244 os.Remove(libgoh)
245 }()
246 }
247
248 ccArgs := append(cc, "-o", exe, "main.c")
249 if GOOS == "windows" {
250 ccArgs = append(ccArgs, "main_windows.c", libgoa, "-lntdll", "-lws2_32", "-lwinmm")
251 } else {
252 ccArgs = append(ccArgs, "main_unix.c", libgoa)
253 }
254 if runtime.Compiler == "gccgo" {
255 ccArgs = append(ccArgs, "-lgo")
256 }
257 t.Log(ccArgs)
258 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
259 t.Logf("%s", out)
260 t.Fatal(err)
261 }
262 if !testWork {
263 defer os.Remove(exe)
264 }
265
266 binArgs := append(cmdToRun(exe), "arg1", "arg2")
267 cmd = exec.Command(binArgs[0], binArgs[1:]...)
268 if runtime.Compiler == "gccgo" {
269 cmd.Env = append(cmd.Environ(), "GCCGO=1")
270 }
271 if out, err := cmd.CombinedOutput(); err != nil {
272 t.Logf("%s", out)
273 t.Fatal(err)
274 }
275
276 checkLineComments(t, libgoh)
277 }
278
279 var badLineRegexp = regexp.MustCompile(`(?m)^#line [0-9]+ "/.*$`)
280
281
282
283
284
285
286 func checkLineComments(t *testing.T, hdrname string) {
287 hdr, err := os.ReadFile(hdrname)
288 if err != nil {
289 if !os.IsNotExist(err) {
290 t.Error(err)
291 }
292 return
293 }
294 if line := badLineRegexp.Find(hdr); line != nil {
295 t.Errorf("bad #line directive with absolute path in %s: %q", hdrname, line)
296 }
297 }
298
299
300
301 func checkArchive(t *testing.T, arname string) {
302 t.Helper()
303
304 switch GOOS {
305 case "aix", "darwin", "ios", "windows":
306
307 if _, err := os.Stat(arname); err != nil {
308 t.Errorf("archive %s does not exist: %v", arname, err)
309 }
310 default:
311 checkELFArchive(t, arname)
312 }
313 }
314
315
316 func checkELFArchive(t *testing.T, arname string) {
317 t.Helper()
318
319 f, err := os.Open(arname)
320 if err != nil {
321 t.Errorf("archive %s does not exist: %v", arname, err)
322 return
323 }
324 defer f.Close()
325
326
327 const (
328 magic = "!<arch>\n"
329 fmag = "`\n"
330
331 namelen = 16
332 datelen = 12
333 uidlen = 6
334 gidlen = 6
335 modelen = 8
336 sizelen = 10
337 fmaglen = 2
338 hdrlen = namelen + datelen + uidlen + gidlen + modelen + sizelen + fmaglen
339 )
340
341 type arhdr struct {
342 name string
343 date string
344 uid string
345 gid string
346 mode string
347 size string
348 fmag string
349 }
350
351 var magbuf [len(magic)]byte
352 if _, err := io.ReadFull(f, magbuf[:]); err != nil {
353 t.Errorf("%s: archive too short", arname)
354 return
355 }
356 if string(magbuf[:]) != magic {
357 t.Errorf("%s: incorrect archive magic string %q", arname, magbuf)
358 }
359
360 off := int64(len(magic))
361 for {
362 if off&1 != 0 {
363 var b [1]byte
364 if _, err := f.Read(b[:]); err != nil {
365 if err == io.EOF {
366 break
367 }
368 t.Errorf("%s: error skipping alignment byte at %d: %v", arname, off, err)
369 }
370 off++
371 }
372
373 var hdrbuf [hdrlen]byte
374 if _, err := io.ReadFull(f, hdrbuf[:]); err != nil {
375 if err == io.EOF {
376 break
377 }
378 t.Errorf("%s: error reading archive header at %d: %v", arname, off, err)
379 return
380 }
381
382 var hdr arhdr
383 hdrslice := hdrbuf[:]
384 set := func(len int, ps *string) {
385 *ps = string(bytes.TrimSpace(hdrslice[:len]))
386 hdrslice = hdrslice[len:]
387 }
388 set(namelen, &hdr.name)
389 set(datelen, &hdr.date)
390 set(uidlen, &hdr.uid)
391 set(gidlen, &hdr.gid)
392 set(modelen, &hdr.mode)
393 set(sizelen, &hdr.size)
394 hdr.fmag = string(hdrslice[:fmaglen])
395 hdrslice = hdrslice[fmaglen:]
396 if len(hdrslice) != 0 {
397 t.Fatalf("internal error: len(hdrslice) == %d", len(hdrslice))
398 }
399
400 if hdr.fmag != fmag {
401 t.Errorf("%s: invalid fmagic value %q at %d", arname, hdr.fmag, off)
402 return
403 }
404
405 size, err := strconv.ParseInt(hdr.size, 10, 64)
406 if err != nil {
407 t.Errorf("%s: error parsing size %q at %d: %v", arname, hdr.size, off, err)
408 return
409 }
410
411 off += hdrlen
412
413 switch hdr.name {
414 case "__.SYMDEF", "/", "/SYM64/":
415
416 case "//", "ARFILENAMES/":
417
418 default:
419
420 checkELFArchiveObject(t, arname, off, io.NewSectionReader(f, off, size))
421 }
422
423 off += size
424 if _, err := f.Seek(off, io.SeekStart); err != nil {
425 t.Errorf("%s: failed to seek to %d: %v", arname, off, err)
426 }
427 }
428 }
429
430
431 func checkELFArchiveObject(t *testing.T, arname string, off int64, obj io.ReaderAt) {
432 t.Helper()
433
434 ef, err := elf.NewFile(obj)
435 if err != nil {
436 t.Errorf("%s: failed to open ELF file at %d: %v", arname, off, err)
437 return
438 }
439 defer ef.Close()
440
441
442 for _, sec := range ef.Sections {
443 want := elf.SHT_NULL
444 switch sec.Name {
445 case ".text", ".data":
446 want = elf.SHT_PROGBITS
447 case ".bss":
448 want = elf.SHT_NOBITS
449 case ".symtab":
450 want = elf.SHT_SYMTAB
451 case ".strtab":
452 want = elf.SHT_STRTAB
453 case ".init_array":
454 want = elf.SHT_INIT_ARRAY
455 case ".fini_array":
456 want = elf.SHT_FINI_ARRAY
457 case ".preinit_array":
458 want = elf.SHT_PREINIT_ARRAY
459 }
460 if want != elf.SHT_NULL && sec.Type != want {
461 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)
462 }
463 }
464 }
465
466 func TestInstall(t *testing.T) {
467 globalSkip(t)
468 testenv.MustHaveGoBuild(t)
469 testenv.MustHaveCGO(t)
470 testenv.MustHaveBuildMode(t, "c-archive")
471
472 if !testWork {
473 defer os.RemoveAll(filepath.Join(GOPATH, "pkg"))
474 }
475
476 libgoa := "libgo.a"
477 if runtime.Compiler == "gccgo" {
478 libgoa = "liblibgo.a"
479 }
480
481
482
483
484
485
486
487 genHeader(t, "p.h", "./p")
488
489 testInstall(t, "./testp1"+exeSuffix,
490 filepath.Join(libgodir, libgoa),
491 filepath.Join(libgodir, "libgo.h"),
492 "go", "install", "-buildmode=c-archive", "./libgo")
493
494
495
496 testInstall(t, "./testp2"+exeSuffix, "libgo.a", "libgo.h",
497 "go", "build", "-buildmode=c-archive", filepath.Join(".", "libgo", "libgo.go"))
498
499 testInstall(t, "./testp3"+exeSuffix, "libgo.a", "libgo.h",
500 "go", "build", "-buildmode=c-archive", "-o", "libgo.a", "./libgo")
501 }
502
503 func TestEarlySignalHandler(t *testing.T) {
504 switch GOOS {
505 case "darwin", "ios":
506 switch GOARCH {
507 case "arm64":
508 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
509 }
510 case "windows":
511 t.Skip("skipping signal test on Windows")
512 }
513 globalSkip(t)
514 testenv.MustHaveGoBuild(t)
515 testenv.MustHaveCGO(t)
516 testenv.MustHaveBuildMode(t, "c-archive")
517
518 if !testWork {
519 defer func() {
520 os.Remove("libgo2.a")
521 os.Remove("libgo2.h")
522 os.Remove("testp" + exeSuffix)
523 os.RemoveAll(filepath.Join(GOPATH, "pkg"))
524 }()
525 }
526
527 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
528 if out, err := cmd.CombinedOutput(); err != nil {
529 t.Logf("%s", out)
530 t.Fatal(err)
531 }
532 checkLineComments(t, "libgo2.h")
533 checkArchive(t, "libgo2.a")
534
535 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a")
536 if runtime.Compiler == "gccgo" {
537 ccArgs = append(ccArgs, "-lgo")
538 }
539 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
540 t.Logf("%s", out)
541 t.Fatal(err)
542 }
543
544 darwin := "0"
545 if runtime.GOOS == "darwin" {
546 darwin = "1"
547 }
548 cmd = exec.Command(bin[0], append(bin[1:], darwin)...)
549
550 if out, err := cmd.CombinedOutput(); err != nil {
551 t.Logf("%s", out)
552 t.Fatal(err)
553 }
554 }
555
556 func TestSignalForwarding(t *testing.T) {
557 globalSkip(t)
558 checkSignalForwardingTest(t)
559 buildSignalForwardingTest(t)
560
561 cmd := exec.Command(bin[0], append(bin[1:], "1")...)
562
563 out, err := cmd.CombinedOutput()
564 t.Logf("%v\n%s", cmd.Args, out)
565 expectSignal(t, err, syscall.SIGSEGV, 0)
566
567
568 if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
569
570 cmd = exec.Command(bin[0], append(bin[1:], "3")...)
571
572 out, err = cmd.CombinedOutput()
573 if len(out) > 0 {
574 t.Logf("%s", out)
575 }
576 expectSignal(t, err, syscall.SIGPIPE, 0)
577 }
578 }
579
580 func TestSignalForwardingExternal(t *testing.T) {
581 if GOOS == "freebsd" || GOOS == "aix" {
582 t.Skipf("skipping on %s/%s; signal always goes to the Go runtime", GOOS, GOARCH)
583 } else if GOOS == "darwin" && GOARCH == "amd64" {
584 t.Skipf("skipping on %s/%s: runtime does not permit SI_USER SIGSEGV", GOOS, GOARCH)
585 }
586 globalSkip(t)
587 checkSignalForwardingTest(t)
588 buildSignalForwardingTest(t)
589
590
591
592
593
594
595
596
597
598
599 const tries = 20
600 for i := 0; i < tries; i++ {
601 err := runSignalForwardingTest(t, "2")
602 if err == nil {
603 continue
604 }
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621 if expectSignal(t, err, syscall.SIGSEGV, syscall.SIGQUIT) {
622 return
623 }
624 }
625
626 t.Errorf("program succeeded unexpectedly %d times", tries)
627 }
628
629 func TestSignalForwardingGo(t *testing.T) {
630
631
632
633 if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" {
634 t.Skip("not supported on darwin-amd64")
635 }
636 globalSkip(t)
637
638 checkSignalForwardingTest(t)
639 buildSignalForwardingTest(t)
640 err := runSignalForwardingTest(t, "4")
641
642
643
644 expectSignal(t, err, syscall.SIGQUIT, syscall.SIGSEGV)
645 }
646
647
648
649 func checkSignalForwardingTest(t *testing.T) {
650 switch GOOS {
651 case "darwin", "ios":
652 switch GOARCH {
653 case "arm64":
654 t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
655 }
656 case "windows":
657 t.Skip("skipping signal test on Windows")
658 }
659 testenv.MustHaveGoBuild(t)
660 testenv.MustHaveCGO(t)
661 testenv.MustHaveBuildMode(t, "c-archive")
662 }
663
664
665
666 func buildSignalForwardingTest(t *testing.T) {
667 if !testWork {
668 t.Cleanup(func() {
669 os.Remove("libgo2.a")
670 os.Remove("libgo2.h")
671 os.Remove("testp" + exeSuffix)
672 os.RemoveAll(filepath.Join(GOPATH, "pkg"))
673 })
674 }
675
676 t.Log("go build -buildmode=c-archive -o libgo2.a ./libgo2")
677 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
678 out, err := cmd.CombinedOutput()
679 if len(out) > 0 {
680 t.Logf("%s", out)
681 }
682 if err != nil {
683 t.Fatal(err)
684 }
685
686 checkLineComments(t, "libgo2.h")
687 checkArchive(t, "libgo2.a")
688
689 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
690 if runtime.Compiler == "gccgo" {
691 ccArgs = append(ccArgs, "-lgo")
692 }
693 t.Log(ccArgs)
694 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
695 if len(out) > 0 {
696 t.Logf("%s", out)
697 }
698 if err != nil {
699 t.Fatal(err)
700 }
701 }
702
703 func runSignalForwardingTest(t *testing.T, arg string) error {
704 t.Logf("%v %s", bin, arg)
705 cmd := exec.Command(bin[0], append(bin[1:], arg)...)
706
707 var out strings.Builder
708 cmd.Stdout = &out
709
710 stderr, err := cmd.StderrPipe()
711 if err != nil {
712 t.Fatal(err)
713 }
714 defer stderr.Close()
715
716 r := bufio.NewReader(stderr)
717
718 err = cmd.Start()
719 if err != nil {
720 t.Fatal(err)
721 }
722
723
724 ok, err := r.ReadString('\n')
725
726
727 if err != nil || ok != "OK\n" {
728 t.Fatal("Did not receive OK signal")
729 }
730
731 var wg sync.WaitGroup
732 wg.Add(1)
733 var errsb strings.Builder
734 go func() {
735 defer wg.Done()
736 io.Copy(&errsb, r)
737 }()
738
739
740
741
742
743 time.Sleep(time.Millisecond)
744
745 cmd.Process.Signal(syscall.SIGSEGV)
746
747 err = cmd.Wait()
748
749 s := out.String()
750 if len(s) > 0 {
751 t.Log(s)
752 }
753 wg.Wait()
754 s = errsb.String()
755 if len(s) > 0 {
756 t.Log(s)
757 }
758
759 return err
760 }
761
762
763
764
765 func expectSignal(t *testing.T, err error, sig1, sig2 syscall.Signal) bool {
766 t.Helper()
767 if err == nil {
768 t.Error("test program succeeded unexpectedly")
769 } else if ee, ok := err.(*exec.ExitError); !ok {
770 t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
771 } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
772 t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
773 } else if !ws.Signaled() || (ws.Signal() != sig1 && ws.Signal() != sig2) {
774 if sig2 == 0 {
775 t.Errorf("got %q; expected signal %q", ee, sig1)
776 } else {
777 t.Errorf("got %q; expected signal %q or %q", ee, sig1, sig2)
778 }
779 } else {
780 return true
781 }
782 return false
783 }
784
785 func TestOsSignal(t *testing.T) {
786 switch GOOS {
787 case "windows":
788 t.Skip("skipping signal test on Windows")
789 }
790 globalSkip(t)
791 testenv.MustHaveGoBuild(t)
792 testenv.MustHaveCGO(t)
793 testenv.MustHaveBuildMode(t, "c-archive")
794
795 if !testWork {
796 defer func() {
797 os.Remove("libgo3.a")
798 os.Remove("libgo3.h")
799 os.Remove("testp" + exeSuffix)
800 os.RemoveAll(filepath.Join(GOPATH, "pkg"))
801 }()
802 }
803
804 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "./libgo3")
805 if out, err := cmd.CombinedOutput(); err != nil {
806 t.Logf("%s", out)
807 t.Fatal(err)
808 }
809 checkLineComments(t, "libgo3.h")
810 checkArchive(t, "libgo3.a")
811
812 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a")
813 if runtime.Compiler == "gccgo" {
814 ccArgs = append(ccArgs, "-lgo")
815 }
816 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
817 t.Logf("%s", out)
818 t.Fatal(err)
819 }
820
821 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
822 t.Logf("%s", out)
823 t.Fatal(err)
824 }
825 }
826
827 func TestSigaltstack(t *testing.T) {
828 switch GOOS {
829 case "windows":
830 t.Skip("skipping signal test on Windows")
831 }
832 globalSkip(t)
833 testenv.MustHaveGoBuild(t)
834 testenv.MustHaveCGO(t)
835 testenv.MustHaveBuildMode(t, "c-archive")
836
837 if !testWork {
838 defer func() {
839 os.Remove("libgo4.a")
840 os.Remove("libgo4.h")
841 os.Remove("testp" + exeSuffix)
842 os.RemoveAll(filepath.Join(GOPATH, "pkg"))
843 }()
844 }
845
846 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "./libgo4")
847 if out, err := cmd.CombinedOutput(); err != nil {
848 t.Logf("%s", out)
849 t.Fatal(err)
850 }
851 checkLineComments(t, "libgo4.h")
852 checkArchive(t, "libgo4.a")
853
854 ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a")
855 if runtime.Compiler == "gccgo" {
856 ccArgs = append(ccArgs, "-lgo")
857 }
858 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
859 t.Logf("%s", out)
860 t.Fatal(err)
861 }
862
863 if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
864 t.Logf("%s", out)
865 t.Fatal(err)
866 }
867 }
868
869 const testar = `#!/usr/bin/env bash
870 while [[ $1 == -* ]] >/dev/null; do
871 shift
872 done
873 echo "testar" > $1
874 echo "testar" > PWD/testar.ran
875 `
876
877 func TestExtar(t *testing.T) {
878 switch GOOS {
879 case "windows":
880 t.Skip("skipping signal test on Windows")
881 }
882 if runtime.Compiler == "gccgo" {
883 t.Skip("skipping -extar test when using gccgo")
884 }
885 globalSkip(t)
886 testenv.MustHaveGoBuild(t)
887 testenv.MustHaveCGO(t)
888 testenv.MustHaveBuildMode(t, "c-archive")
889 testenv.MustHaveExecPath(t, "bash")
890
891 if !testWork {
892 defer func() {
893 os.Remove("libgo4.a")
894 os.Remove("libgo4.h")
895 os.Remove("testar")
896 os.Remove("testar.ran")
897 os.RemoveAll(filepath.Join(GOPATH, "pkg"))
898 }()
899 }
900
901 os.Remove("testar")
902 dir, err := os.Getwd()
903 if err != nil {
904 t.Fatal(err)
905 }
906 s := strings.Replace(testar, "PWD", dir, 1)
907 if err := os.WriteFile("testar", []byte(s), 0777); err != nil {
908 t.Fatal(err)
909 }
910
911 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "./libgo4")
912 if out, err := cmd.CombinedOutput(); err != nil {
913 t.Logf("%s", out)
914 t.Fatal(err)
915 }
916 checkLineComments(t, "libgo4.h")
917
918 if _, err := os.Stat("testar.ran"); err != nil {
919 if os.IsNotExist(err) {
920 t.Error("testar does not exist after go build")
921 } else {
922 t.Errorf("error checking testar: %v", err)
923 }
924 }
925 }
926
927 func TestPIE(t *testing.T) {
928 switch GOOS {
929 case "windows", "darwin", "ios", "plan9":
930 t.Skipf("skipping PIE test on %s", GOOS)
931 }
932 globalSkip(t)
933 testenv.MustHaveGoBuild(t)
934 testenv.MustHaveCGO(t)
935 testenv.MustHaveBuildMode(t, "c-archive")
936
937 libgoa := "libgo.a"
938 if runtime.Compiler == "gccgo" {
939 libgoa = "liblibgo.a"
940 }
941
942 if !testWork {
943 defer func() {
944 os.Remove("testp" + exeSuffix)
945 os.Remove(libgoa)
946 os.RemoveAll(filepath.Join(GOPATH, "pkg"))
947 }()
948 }
949
950
951
952
953
954
955
956 genHeader(t, "p.h", "./p")
957
958 cmd := exec.Command("go", "build", "-buildmode=c-archive", "./libgo")
959 if out, err := cmd.CombinedOutput(); err != nil {
960 t.Logf("%s", out)
961 t.Fatal(err)
962 }
963
964 ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", libgoa)
965 if runtime.Compiler == "gccgo" {
966 ccArgs = append(ccArgs, "-lgo")
967 }
968 if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
969 t.Logf("%s", out)
970 t.Fatal(err)
971 }
972
973 binArgs := append(bin, "arg1", "arg2")
974 cmd = exec.Command(binArgs[0], binArgs[1:]...)
975 if runtime.Compiler == "gccgo" {
976 cmd.Env = append(os.Environ(), "GCCGO=1")
977 }
978 if out, err := cmd.CombinedOutput(); err != nil {
979 t.Logf("%s", out)
980 t.Fatal(err)
981 }
982
983 if GOOS != "aix" {
984 f, err := elf.Open("testp" + exeSuffix)
985 if err != nil {
986 t.Fatal("elf.Open failed: ", err)
987 }
988 defer f.Close()
989 if hasDynTag(t, f, elf.DT_TEXTREL) {
990 t.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix)
991 }
992 }
993 }
994
995 func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool {
996 ds := f.SectionByType(elf.SHT_DYNAMIC)
997 if ds == nil {
998 t.Error("no SHT_DYNAMIC section")
999 return false
1000 }
1001 d, err := ds.Data()
1002 if err != nil {
1003 t.Errorf("can't read SHT_DYNAMIC contents: %v", err)
1004 return false
1005 }
1006 for len(d) > 0 {
1007 var t elf.DynTag
1008 switch f.Class {
1009 case elf.ELFCLASS32:
1010 t = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
1011 d = d[8:]
1012 case elf.ELFCLASS64:
1013 t = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
1014 d = d[16:]
1015 }
1016 if t == tag {
1017 return true
1018 }
1019 }
1020 return false
1021 }
1022
1023 func TestSIGPROF(t *testing.T) {
1024 switch GOOS {
1025 case "windows", "plan9":
1026 t.Skipf("skipping SIGPROF test on %s", GOOS)
1027 case "darwin", "ios":
1028 t.Skipf("skipping SIGPROF test on %s; see https://golang.org/issue/19320", GOOS)
1029 }
1030 globalSkip(t)
1031 testenv.MustHaveGoBuild(t)
1032 testenv.MustHaveCGO(t)
1033 testenv.MustHaveBuildMode(t, "c-archive")
1034
1035 t.Parallel()
1036
1037 if !testWork {
1038 defer func() {
1039 os.Remove("testp6" + exeSuffix)
1040 os.Remove("libgo6.a")
1041 os.Remove("libgo6.h")
1042 }()
1043 }
1044
1045 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6")
1046 out, err := cmd.CombinedOutput()
1047 t.Logf("%v\n%s", cmd.Args, out)
1048 if err != nil {
1049 t.Fatal(err)
1050 }
1051 checkLineComments(t, "libgo6.h")
1052 checkArchive(t, "libgo6.a")
1053
1054 ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a")
1055 if runtime.Compiler == "gccgo" {
1056 ccArgs = append(ccArgs, "-lgo")
1057 }
1058 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
1059 t.Logf("%v\n%s", ccArgs, out)
1060 if err != nil {
1061 t.Fatal(err)
1062 }
1063
1064 argv := cmdToRun("./testp6")
1065 cmd = exec.Command(argv[0], argv[1:]...)
1066 out, err = cmd.CombinedOutput()
1067 t.Logf("%v\n%s", argv, out)
1068 if err != nil {
1069 t.Fatal(err)
1070 }
1071 }
1072
1073
1074
1075
1076
1077
1078
1079 func TestCompileWithoutShared(t *testing.T) {
1080 globalSkip(t)
1081
1082 checkSignalForwardingTest(t)
1083 testenv.MustHaveGoBuild(t)
1084
1085 if !testWork {
1086 defer func() {
1087 os.Remove("libgo2.a")
1088 os.Remove("libgo2.h")
1089 }()
1090 }
1091
1092 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2")
1093 out, err := cmd.CombinedOutput()
1094 t.Logf("%v\n%s", cmd.Args, out)
1095 if err != nil {
1096 t.Fatal(err)
1097 }
1098 checkLineComments(t, "libgo2.h")
1099 checkArchive(t, "libgo2.a")
1100
1101 exe := "./testnoshared" + exeSuffix
1102
1103
1104
1105 ccArgs := append(cc, "-o", exe, "-no-pie", "main5.c", "libgo2.a")
1106 if runtime.Compiler == "gccgo" {
1107 ccArgs = append(ccArgs, "-lgo")
1108 }
1109 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
1110 t.Logf("%v\n%s", ccArgs, out)
1111
1112
1113 if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") {
1114 ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a")
1115 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
1116 t.Logf("%v\n%s", ccArgs, out)
1117 }
1118
1119
1120 if err != nil && bytes.Contains(out, []byte("unrecognized")) {
1121 ccArgs = append(cc, "-o", exe, "main5.c", "libgo2.a")
1122 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
1123 t.Logf("%v\n%s", ccArgs, out)
1124 }
1125 if err != nil {
1126 t.Fatal(err)
1127 }
1128 if !testWork {
1129 defer os.Remove(exe)
1130 }
1131
1132 binArgs := append(cmdToRun(exe), "1")
1133 out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
1134 t.Logf("%v\n%s", binArgs, out)
1135 expectSignal(t, err, syscall.SIGSEGV, 0)
1136
1137
1138 if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
1139 binArgs := append(cmdToRun(exe), "3")
1140 out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
1141 t.Logf("%v\n%s", binArgs, out)
1142 expectSignal(t, err, syscall.SIGPIPE, 0)
1143 }
1144 }
1145
1146
1147 func TestCachedInstall(t *testing.T) {
1148 globalSkip(t)
1149 testenv.MustHaveGoBuild(t)
1150 testenv.MustHaveCGO(t)
1151 testenv.MustHaveBuildMode(t, "c-archive")
1152
1153 if !testWork {
1154 defer os.RemoveAll(filepath.Join(GOPATH, "pkg"))
1155 }
1156
1157 h := filepath.Join(libgodir, "libgo.h")
1158
1159 buildcmd := []string{"go", "install", "-buildmode=c-archive", "./libgo"}
1160
1161 cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
1162 cmd.Env = append(cmd.Environ(), "GO111MODULE=off")
1163 t.Log(buildcmd)
1164 if out, err := cmd.CombinedOutput(); err != nil {
1165 t.Logf("%s", out)
1166 t.Fatal(err)
1167 }
1168
1169 if _, err := os.Stat(h); err != nil {
1170 t.Errorf("libgo.h not installed: %v", err)
1171 }
1172
1173 if err := os.Remove(h); err != nil {
1174 t.Fatal(err)
1175 }
1176
1177 cmd = exec.Command(buildcmd[0], buildcmd[1:]...)
1178 cmd.Env = append(cmd.Environ(), "GO111MODULE=off")
1179 t.Log(buildcmd)
1180 if out, err := cmd.CombinedOutput(); err != nil {
1181 t.Logf("%s", out)
1182 t.Fatal(err)
1183 }
1184
1185 if _, err := os.Stat(h); err != nil {
1186 t.Errorf("libgo.h not installed in second run: %v", err)
1187 }
1188 }
1189
1190
1191 func TestManyCalls(t *testing.T) {
1192 globalSkip(t)
1193 testenv.MustHaveGoBuild(t)
1194 testenv.MustHaveCGO(t)
1195 testenv.MustHaveBuildMode(t, "c-archive")
1196
1197 t.Parallel()
1198
1199 if !testWork {
1200 defer func() {
1201 os.Remove("testp7" + exeSuffix)
1202 os.Remove("libgo7.a")
1203 os.Remove("libgo7.h")
1204 }()
1205 }
1206
1207 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7")
1208 out, err := cmd.CombinedOutput()
1209 t.Logf("%v\n%s", cmd.Args, out)
1210 if err != nil {
1211 t.Fatal(err)
1212 }
1213 checkLineComments(t, "libgo7.h")
1214 checkArchive(t, "libgo7.a")
1215
1216 ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a")
1217 if runtime.Compiler == "gccgo" {
1218 ccArgs = append(ccArgs, "-lgo")
1219 }
1220 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
1221 t.Logf("%v\n%s", ccArgs, out)
1222 if err != nil {
1223 t.Fatal(err)
1224 }
1225
1226 argv := cmdToRun("./testp7")
1227 cmd = testenv.Command(t, argv[0], argv[1:]...)
1228 sb := new(strings.Builder)
1229 cmd.Stdout = sb
1230 cmd.Stderr = sb
1231 if err := cmd.Start(); err != nil {
1232 t.Fatal(err)
1233 }
1234
1235 err = cmd.Wait()
1236 t.Logf("%v\n%s", cmd.Args, sb)
1237 if err != nil {
1238 t.Error(err)
1239 }
1240 }
1241
1242
1243 func TestPreemption(t *testing.T) {
1244 if runtime.Compiler == "gccgo" {
1245 t.Skip("skipping asynchronous preemption test with gccgo")
1246 }
1247 globalSkip(t)
1248 testenv.MustHaveGoBuild(t)
1249 testenv.MustHaveCGO(t)
1250 testenv.MustHaveBuildMode(t, "c-archive")
1251
1252 t.Parallel()
1253
1254 if !testWork {
1255 defer func() {
1256 os.Remove("testp8" + exeSuffix)
1257 os.Remove("libgo8.a")
1258 os.Remove("libgo8.h")
1259 }()
1260 }
1261
1262 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8")
1263 out, err := cmd.CombinedOutput()
1264 t.Logf("%v\n%s", cmd.Args, out)
1265 if err != nil {
1266 t.Fatal(err)
1267 }
1268 checkLineComments(t, "libgo8.h")
1269 checkArchive(t, "libgo8.a")
1270
1271 ccArgs := append(cc, "-o", "testp8"+exeSuffix, "main8.c", "libgo8.a")
1272 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
1273 t.Logf("%v\n%s", ccArgs, out)
1274 if err != nil {
1275 t.Fatal(err)
1276 }
1277
1278 argv := cmdToRun("./testp8")
1279 cmd = testenv.Command(t, argv[0], argv[1:]...)
1280 sb := new(strings.Builder)
1281 cmd.Stdout = sb
1282 cmd.Stderr = sb
1283 if err := cmd.Start(); err != nil {
1284 t.Fatal(err)
1285 }
1286
1287 err = cmd.Wait()
1288 t.Logf("%v\n%s", cmd.Args, sb)
1289 if err != nil {
1290 t.Error(err)
1291 }
1292 }
1293
1294
1295
1296 func TestDeepStack(t *testing.T) {
1297 globalSkip(t)
1298 testenv.MustHaveGoBuild(t)
1299 testenv.MustHaveCGO(t)
1300 testenv.MustHaveBuildMode(t, "c-archive")
1301
1302 t.Parallel()
1303
1304 if !testWork {
1305 defer func() {
1306 os.Remove("testp9" + exeSuffix)
1307 os.Remove("libgo9.a")
1308 os.Remove("libgo9.h")
1309 }()
1310 }
1311
1312 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo9.a", "./libgo9")
1313 out, err := cmd.CombinedOutput()
1314 t.Logf("%v\n%s", cmd.Args, out)
1315 if err != nil {
1316 t.Fatal(err)
1317 }
1318 checkLineComments(t, "libgo9.h")
1319 checkArchive(t, "libgo9.a")
1320
1321
1322 ccArgs := append(cc, "-O0", "-o", "testp9"+exeSuffix, "main9.c", "libgo9.a")
1323 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
1324 t.Logf("%v\n%s", ccArgs, out)
1325 if err != nil {
1326 t.Fatal(err)
1327 }
1328
1329 argv := cmdToRun("./testp9")
1330 cmd = exec.Command(argv[0], argv[1:]...)
1331 sb := new(strings.Builder)
1332 cmd.Stdout = sb
1333 cmd.Stderr = sb
1334 if err := cmd.Start(); err != nil {
1335 t.Fatal(err)
1336 }
1337
1338 timer := time.AfterFunc(time.Minute,
1339 func() {
1340 t.Error("test program timed out")
1341 cmd.Process.Kill()
1342 },
1343 )
1344 defer timer.Stop()
1345
1346 err = cmd.Wait()
1347 t.Logf("%v\n%s", cmd.Args, sb)
1348 if err != nil {
1349 t.Error(err)
1350 }
1351 }
1352
1353 func BenchmarkCgoCallbackMainThread(b *testing.B) {
1354
1355
1356
1357
1358
1359
1360
1361
1362 globalSkip(b)
1363 testenv.MustHaveGoBuild(b)
1364 testenv.MustHaveCGO(b)
1365 testenv.MustHaveBuildMode(b, "c-archive")
1366
1367 if !testWork {
1368 defer func() {
1369 os.Remove("testp10" + exeSuffix)
1370 os.Remove("libgo10.a")
1371 os.Remove("libgo10.h")
1372 }()
1373 }
1374
1375 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo10.a", "./libgo10")
1376 out, err := cmd.CombinedOutput()
1377 b.Logf("%v\n%s", cmd.Args, out)
1378 if err != nil {
1379 b.Fatal(err)
1380 }
1381
1382 ccArgs := append(cc, "-o", "testp10"+exeSuffix, "main10.c", "libgo10.a")
1383 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
1384 b.Logf("%v\n%s", ccArgs, out)
1385 if err != nil {
1386 b.Fatal(err)
1387 }
1388
1389 argv := cmdToRun("./testp10")
1390 argv = append(argv, fmt.Sprint(b.N))
1391 cmd = exec.Command(argv[0], argv[1:]...)
1392
1393 b.ResetTimer()
1394 err = cmd.Run()
1395 if err != nil {
1396 b.Fatal(err)
1397 }
1398 }
1399
1400 func TestSharedObject(t *testing.T) {
1401
1402 globalSkip(t)
1403 testenv.MustHaveGoBuild(t)
1404 testenv.MustHaveCGO(t)
1405 testenv.MustHaveBuildMode(t, "c-archive")
1406
1407 t.Parallel()
1408
1409 if !testWork {
1410 defer func() {
1411 os.Remove("libgo_s.a")
1412 os.Remove("libgo_s.h")
1413 os.Remove("libgo_s.so")
1414 }()
1415 }
1416
1417 cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo_s.a", "./libgo")
1418 out, err := cmd.CombinedOutput()
1419 t.Logf("%v\n%s", cmd.Args, out)
1420 if err != nil {
1421 t.Fatal(err)
1422 }
1423
1424 ccArgs := append(cc, "-shared", "-o", "libgo_s.so", "libgo_s.a")
1425 out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
1426 t.Logf("%v\n%s", ccArgs, out)
1427 if err != nil {
1428 t.Fatal(err)
1429 }
1430 }
1431
View as plain text