1
2
3
4
5 package cshared_test
6
7 import (
8 "bufio"
9 "bytes"
10 "cmd/cgo/internal/cgotest"
11 "cmp"
12 "debug/elf"
13 "debug/pe"
14 "encoding/binary"
15 "flag"
16 "fmt"
17 "internal/testenv"
18 "log"
19 "os"
20 "os/exec"
21 "path/filepath"
22 "runtime"
23 "strings"
24 "sync"
25 "testing"
26 "unicode"
27 )
28
29 var globalSkip = func(t *testing.T) {}
30
31
32 var cc []string
33
34
35 var exeSuffix string
36
37 var GOOS, GOARCH, GOROOT string
38 var installdir string
39 var libgoname string
40
41 func TestMain(m *testing.M) {
42 os.Exit(testMain(m))
43 }
44
45 func testMain(m *testing.M) int {
46 log.SetFlags(log.Lshortfile)
47 flag.Parse()
48 if testing.Short() && testenv.Builder() == "" {
49 globalSkip = func(t *testing.T) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
50 return m.Run()
51 }
52 if runtime.GOOS == "linux" {
53 if _, err := os.Stat("/etc/alpine-release"); err == nil {
54 globalSkip = func(t *testing.T) { t.Skip("skipping failing test on alpine - go.dev/issue/19938") }
55 return m.Run()
56 }
57 }
58 if !testenv.HasGoBuild() {
59
60 globalSkip = func(t *testing.T) { t.Skip("no go build") }
61 return m.Run()
62 }
63
64 GOOS = goEnv("GOOS")
65 GOARCH = goEnv("GOARCH")
66 GOROOT = goEnv("GOROOT")
67
68 if _, err := os.Stat(GOROOT); os.IsNotExist(err) {
69 log.Fatalf("Unable able to find GOROOT at '%s'", GOROOT)
70 }
71
72 cc = []string{goEnv("CC")}
73
74 out := goEnv("GOGCCFLAGS")
75 quote := '\000'
76 start := 0
77 lastSpace := true
78 backslash := false
79 s := out
80 for i, c := range s {
81 if quote == '\000' && unicode.IsSpace(c) {
82 if !lastSpace {
83 cc = append(cc, s[start:i])
84 lastSpace = true
85 }
86 } else {
87 if lastSpace {
88 start = i
89 lastSpace = false
90 }
91 if quote == '\000' && !backslash && (c == '"' || c == '\'') {
92 quote = c
93 backslash = false
94 } else if !backslash && quote == c {
95 quote = '\000'
96 } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
97 backslash = true
98 } else {
99 backslash = false
100 }
101 }
102 }
103 if !lastSpace {
104 cc = append(cc, s[start:])
105 }
106
107 switch GOOS {
108 case "darwin", "ios":
109
110
111 cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
112 case "android":
113 cc = append(cc, "-pie")
114 }
115 libgodir := GOOS + "_" + GOARCH
116 switch GOOS {
117 case "darwin", "ios":
118 if GOARCH == "arm64" {
119 libgodir += "_shared"
120 }
121 case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris", "illumos":
122 libgodir += "_shared"
123 }
124 cc = append(cc, "-I", filepath.Join("pkg", libgodir))
125
126
127 cc = cc[:len(cc):len(cc)]
128
129 if GOOS == "windows" {
130 exeSuffix = ".exe"
131 }
132
133
134
135
136 GOPATH, err := os.MkdirTemp("", "cshared_test")
137 if err != nil {
138 log.Panic(err)
139 }
140 defer os.RemoveAll(GOPATH)
141 os.Setenv("GOPATH", GOPATH)
142
143 modRoot := filepath.Join(GOPATH, "src", "testcshared")
144 if err := cgotest.OverlayDir(modRoot, "testdata"); err != nil {
145 log.Panic(err)
146 }
147 if err := os.Chdir(modRoot); err != nil {
148 log.Panic(err)
149 }
150 os.Setenv("PWD", modRoot)
151 if err := os.WriteFile("go.mod", []byte("module testcshared\n"), 0666); err != nil {
152 log.Panic(err)
153 }
154
155 defer func() {
156 if installdir != "" {
157 err := os.RemoveAll(installdir)
158 if err != nil {
159 log.Panic(err)
160 }
161 }
162 }()
163
164 return m.Run()
165 }
166
167 func goEnv(key string) string {
168 out, err := exec.Command("go", "env", key).Output()
169 if err != nil {
170 log.Printf("go env %s failed:\n%s", key, err)
171 log.Panicf("%s", err.(*exec.ExitError).Stderr)
172 }
173 return strings.TrimSpace(string(out))
174 }
175
176 func cmdToRun(name string) string {
177 return "./" + name + exeSuffix
178 }
179
180 func run(t *testing.T, extraEnv []string, args ...string) string {
181 t.Helper()
182 cmd := exec.Command(args[0], args[1:]...)
183 if len(extraEnv) > 0 {
184 cmd.Env = append(os.Environ(), extraEnv...)
185 }
186 stderr := new(strings.Builder)
187 cmd.Stderr = stderr
188
189 if GOOS != "windows" {
190
191
192
193
194
195 cmd.ExtraFiles = make([]*os.File, 28)
196 }
197
198 t.Logf("run: %v", args)
199 out, err := cmd.Output()
200 if stderr.Len() > 0 {
201 t.Logf("stderr:\n%s", stderr)
202 }
203 if err != nil {
204 t.Fatalf("command failed: %v\n%v\n%s\n", args, err, out)
205 }
206 return string(out)
207 }
208
209 func runExe(t *testing.T, extraEnv []string, args ...string) string {
210 t.Helper()
211 return run(t, extraEnv, args...)
212 }
213
214 func runCC(t *testing.T, args ...string) string {
215 t.Helper()
216
217
218 return run(t, nil, append(append([]string(nil), cc...), args...)...)
219 }
220
221 func createHeaders() error {
222
223
224
225 objDir, err := os.MkdirTemp("", "testcshared_obj")
226 if err != nil {
227 return err
228 }
229 defer os.RemoveAll(objDir)
230
231
232
233
234
235 args := []string{"go", "tool", "cgo",
236 "-objdir", objDir,
237 "-exportheader", "p.h",
238 filepath.Join(".", "p", "p.go")}
239 cmd := exec.Command(args[0], args[1:]...)
240 out, err := cmd.CombinedOutput()
241 if err != nil {
242 return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
243 }
244
245
246 installdir, err = os.MkdirTemp("", "testcshared")
247 if err != nil {
248 return err
249 }
250 libgoname = "libgo.a"
251
252 args = []string{"go", "build", "-buildmode=c-shared", "-o", filepath.Join(installdir, libgoname), "./libgo"}
253 cmd = exec.Command(args[0], args[1:]...)
254 out, err = cmd.CombinedOutput()
255 if err != nil {
256 return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
257 }
258
259 args = []string{"go", "build", "-buildmode=c-shared",
260 "-installsuffix", "testcshared",
261 "-o", libgoname,
262 filepath.Join(".", "libgo", "libgo.go")}
263 if GOOS == "windows" && strings.HasSuffix(args[6], ".a") {
264 args[6] = strings.TrimSuffix(args[6], ".a") + ".dll"
265 }
266 cmd = exec.Command(args[0], args[1:]...)
267 out, err = cmd.CombinedOutput()
268 if err != nil {
269 return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
270 }
271 if GOOS == "windows" {
272
273
274
275 err = os.WriteFile("libgo.def",
276 []byte("LIBRARY libgo.dll\nEXPORTS\n\tDidInitRun\n\tDidMainRun\n\tDivu\n\tFromPkg\n"),
277 0644)
278 if err != nil {
279 return fmt.Errorf("unable to write def file: %v", err)
280 }
281 out, err = exec.Command(cc[0], append(cc[1:], "-print-prog-name=dlltool")...).CombinedOutput()
282 if err != nil {
283 return fmt.Errorf("unable to find dlltool path: %v\n%s\n", err, out)
284 }
285 dlltoolpath := strings.TrimSpace(string(out))
286 if filepath.Ext(dlltoolpath) == "" {
287
288
289
290 if lp, err := exec.LookPath(dlltoolpath); err == nil {
291 dlltoolpath = lp
292 }
293 }
294
295 args := []string{dlltoolpath, "-D", args[6], "-l", libgoname, "-d", "libgo.def"}
296
297 if filepath.Ext(dlltoolpath) == "" {
298
299
300
301
302
303
304 dlltoolContents, err := os.ReadFile(args[0])
305 if err != nil {
306 return fmt.Errorf("unable to read dlltool: %v\n", err)
307 }
308 if bytes.HasPrefix(dlltoolContents, []byte("#!/bin/sh")) && bytes.Contains(dlltoolContents, []byte("llvm-dlltool")) {
309 base, name := filepath.Split(args[0])
310 args[0] = filepath.Join(base, "llvm-dlltool")
311 var machine string
312 switch prefix, _, _ := strings.Cut(name, "-"); prefix {
313 case "i686":
314 machine = "i386"
315 case "x86_64":
316 machine = "i386:x86-64"
317 case "armv7":
318 machine = "arm"
319 case "aarch64":
320 machine = "arm64"
321 }
322 if len(machine) > 0 {
323 args = append(args, "-m", machine)
324 }
325 }
326 }
327
328 out, err = exec.Command(args[0], args[1:]...).CombinedOutput()
329 if err != nil {
330 return fmt.Errorf("unable to run dlltool to create import library: %v\n%s\n", err, out)
331 }
332 }
333
334 return nil
335 }
336
337 var (
338 headersOnce sync.Once
339 headersErr error
340 )
341
342 func createHeadersOnce(t *testing.T) {
343 testenv.MustHaveGoBuild(t)
344 testenv.MustHaveCGO(t)
345 testenv.MustHaveBuildMode(t, "c-shared")
346
347 headersOnce.Do(func() {
348 headersErr = createHeaders()
349 })
350 if headersErr != nil {
351 t.Helper()
352 t.Fatal(headersErr)
353 }
354 }
355
356
357 func TestExportedSymbols(t *testing.T) {
358 globalSkip(t)
359 testenv.MustHaveCGO(t)
360 testenv.MustHaveExec(t)
361
362 t.Parallel()
363
364 cmd := "testp0"
365 bin := cmdToRun(cmd)
366
367 createHeadersOnce(t)
368
369 runCC(t, "-I", installdir, "-o", cmd, "main0.c", libgoname)
370
371 defer os.Remove(bin)
372
373 out := runExe(t, []string{"LD_LIBRARY_PATH=."}, bin)
374 if strings.TrimSpace(out) != "PASS" {
375 t.Error(out)
376 }
377 }
378
379 func checkNumberOfExportedSymbolsWindows(t *testing.T, exportedSymbols int, wantAll bool) {
380 t.Parallel()
381 tmpdir := t.TempDir()
382
383 prog := `
384 package main
385 import "C"
386 func main() {}
387 `
388
389 for i := range exportedSymbols {
390 prog += fmt.Sprintf(`
391 //export GoFunc%d
392 func GoFunc%d() {}
393 `, i, i)
394 }
395
396 srcfile := filepath.Join(tmpdir, "test.go")
397 objfile := filepath.Join(tmpdir, "test.dll")
398 if err := os.WriteFile(srcfile, []byte(prog), 0666); err != nil {
399 t.Fatal(err)
400 }
401 argv := []string{"build", "-buildmode=c-shared"}
402 if wantAll {
403 argv = append(argv, "-ldflags", "-extldflags=-Wl,--export-all-symbols")
404 }
405 argv = append(argv, "-o", objfile, srcfile)
406 out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput()
407 if err != nil {
408 t.Fatalf("build failure: %s\n%s\n", err, string(out))
409 }
410
411 f, err := pe.Open(objfile)
412 if err != nil {
413 t.Fatalf("pe.Open failed: %v", err)
414 }
415 defer f.Close()
416
417 _, pe64 := f.OptionalHeader.(*pe.OptionalHeader64)
418
419 var idd pe.DataDirectory
420 if pe64 {
421 idd = f.OptionalHeader.(*pe.OptionalHeader64).DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT]
422 } else {
423 idd = f.OptionalHeader.(*pe.OptionalHeader32).DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT]
424 }
425
426
427 var section *pe.Section
428 for _, s := range f.Sections {
429 if s.Offset == 0 {
430 continue
431 }
432 if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress-s.VirtualAddress < s.VirtualSize {
433 section = s
434 break
435 }
436 }
437 if section == nil {
438 t.Fatal("no section contains export directory")
439 }
440 d, err := section.Data()
441 if err != nil {
442 t.Fatal(err)
443 }
444
445 d = d[idd.VirtualAddress-section.VirtualAddress:]
446
447
448 type IMAGE_EXPORT_DIRECTORY struct {
449 _ [2]uint32
450 _ [2]uint16
451 _ [2]uint32
452 NumberOfFunctions uint32
453 NumberOfNames uint32
454 _ [3]uint32
455 }
456 var e IMAGE_EXPORT_DIRECTORY
457 if err := binary.Read(bytes.NewReader(d), binary.LittleEndian, &e); err != nil {
458 t.Fatalf("binary.Read failed: %v", err)
459 }
460
461 exportedSymbols = cmp.Or(exportedSymbols, 1)
462
463
464
465
466
467 if wantAll {
468 if e.NumberOfNames <= uint32(exportedSymbols) {
469 t.Errorf("got %d exported names, want > %d", e.NumberOfNames, exportedSymbols)
470 }
471 } else {
472 if e.NumberOfNames != uint32(exportedSymbols) {
473 t.Errorf("got %d exported names, want %d", e.NumberOfNames, exportedSymbols)
474 }
475 }
476 }
477
478 func TestNumberOfExportedFunctions(t *testing.T) {
479 if GOOS != "windows" {
480 t.Skip("skipping windows only test")
481 }
482 globalSkip(t)
483 testenv.MustHaveGoBuild(t)
484 testenv.MustHaveCGO(t)
485 testenv.MustHaveBuildMode(t, "c-shared")
486
487 t.Parallel()
488
489 for i := range 3 {
490 t.Run(fmt.Sprintf("OnlyExported/%d", i), func(t *testing.T) {
491 checkNumberOfExportedSymbolsWindows(t, i, false)
492 })
493 t.Run(fmt.Sprintf("All/%d", i), func(t *testing.T) {
494 checkNumberOfExportedSymbolsWindows(t, i, true)
495 })
496 }
497 }
498
499
500 func TestExportedSymbolsWithDynamicLoad(t *testing.T) {
501 if GOOS == "windows" {
502 t.Skipf("Skipping on %s", GOOS)
503 }
504 globalSkip(t)
505 testenv.MustHaveCGO(t)
506 testenv.MustHaveExec(t)
507
508 t.Parallel()
509
510 cmd := "testp1"
511 bin := cmdToRun(cmd)
512
513 createHeadersOnce(t)
514
515 if GOOS != "freebsd" {
516 runCC(t, "-o", cmd, "main1.c", "-ldl")
517 } else {
518 runCC(t, "-o", cmd, "main1.c")
519 }
520
521 defer os.Remove(bin)
522
523 out := runExe(t, nil, bin, "./"+libgoname)
524 if strings.TrimSpace(out) != "PASS" {
525 t.Error(out)
526 }
527 }
528
529
530 func TestUnexportedSymbols(t *testing.T) {
531 if GOOS == "windows" {
532 t.Skipf("Skipping on %s", GOOS)
533 }
534 globalSkip(t)
535 testenv.MustHaveGoBuild(t)
536 testenv.MustHaveCGO(t)
537 testenv.MustHaveBuildMode(t, "c-shared")
538
539 t.Parallel()
540
541 cmd := "testp2"
542 bin := cmdToRun(cmd)
543 libname := "libgo2.a"
544
545 run(t,
546 nil,
547 "go", "build",
548 "-buildmode=c-shared",
549 "-installsuffix", "testcshared",
550 "-o", libname, "./libgo2",
551 )
552
553 linkFlags := "-Wl,--no-as-needed"
554 if GOOS == "darwin" || GOOS == "ios" {
555 linkFlags = ""
556 }
557
558 runCC(t, "-o", cmd, "main2.c", linkFlags, libname)
559
560 defer os.Remove(libname)
561 defer os.Remove(bin)
562
563 out := runExe(t, []string{"LD_LIBRARY_PATH=."}, bin)
564
565 if strings.TrimSpace(out) != "PASS" {
566 t.Error(out)
567 }
568 }
569
570
571 func TestMainExportedOnAndroid(t *testing.T) {
572 globalSkip(t)
573 testenv.MustHaveCGO(t)
574 testenv.MustHaveExec(t)
575
576 t.Parallel()
577
578 switch GOOS {
579 case "android":
580 break
581 default:
582 t.Logf("Skipping on %s", GOOS)
583 return
584 }
585
586 cmd := "testp3"
587 bin := cmdToRun(cmd)
588
589 createHeadersOnce(t)
590
591 runCC(t, "-o", cmd, "main3.c", "-ldl")
592
593 defer os.Remove(bin)
594
595 out := runExe(t, nil, bin, "./"+libgoname)
596 if strings.TrimSpace(out) != "PASS" {
597 t.Error(out)
598 }
599 }
600
601 func testSignalHandlers(t *testing.T, pkgname, cfile, cmd string) {
602 if GOOS == "windows" {
603 t.Skipf("Skipping on %s", GOOS)
604 }
605 globalSkip(t)
606 testenv.MustHaveGoBuild(t)
607 testenv.MustHaveCGO(t)
608 testenv.MustHaveBuildMode(t, "c-shared")
609
610 libname := pkgname + ".a"
611 run(t,
612 nil,
613 "go", "build",
614 "-buildmode=c-shared",
615 "-installsuffix", "testcshared",
616 "-o", libname, pkgname,
617 )
618 if GOOS != "freebsd" {
619 runCC(t, "-pthread", "-o", cmd, cfile, "-ldl")
620 } else {
621 runCC(t, "-pthread", "-o", cmd, cfile)
622 }
623
624 bin := cmdToRun(cmd)
625
626 defer os.Remove(libname)
627 defer os.Remove(bin)
628 defer os.Remove(pkgname + ".h")
629
630 args := []string{bin, "./" + libname}
631 if testing.Verbose() {
632 args = append(args, "verbose")
633 }
634 out := runExe(t, nil, args...)
635 if strings.TrimSpace(out) != "PASS" {
636 t.Errorf("%v%s", args, out)
637 }
638 }
639
640
641 func TestSignalHandlers(t *testing.T) {
642 t.Parallel()
643 testSignalHandlers(t, "./libgo4", "main4.c", "testp4")
644 }
645
646
647 func TestSignalHandlersWithNotify(t *testing.T) {
648 t.Parallel()
649 testSignalHandlers(t, "./libgo5", "main5.c", "testp5")
650 }
651
652 func TestPIE(t *testing.T) {
653 switch GOOS {
654 case "linux", "android":
655 break
656 default:
657 t.Skipf("Skipping on %s", GOOS)
658 }
659 globalSkip(t)
660
661 t.Parallel()
662
663 createHeadersOnce(t)
664
665 f, err := elf.Open(libgoname)
666 if err != nil {
667 t.Fatalf("elf.Open failed: %v", err)
668 }
669 defer f.Close()
670
671 ds := f.SectionByType(elf.SHT_DYNAMIC)
672 if ds == nil {
673 t.Fatalf("no SHT_DYNAMIC section")
674 }
675 d, err := ds.Data()
676 if err != nil {
677 t.Fatalf("can't read SHT_DYNAMIC contents: %v", err)
678 }
679 for len(d) > 0 {
680 var tag elf.DynTag
681 switch f.Class {
682 case elf.ELFCLASS32:
683 tag = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
684 d = d[8:]
685 case elf.ELFCLASS64:
686 tag = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
687 d = d[16:]
688 }
689 if tag == elf.DT_TEXTREL {
690 t.Fatalf("%s has DT_TEXTREL flag", libgoname)
691 }
692 }
693 }
694
695
696 func TestCachedInstall(t *testing.T) {
697 globalSkip(t)
698 testenv.MustHaveGoBuild(t)
699 testenv.MustHaveCGO(t)
700 testenv.MustHaveBuildMode(t, "c-shared")
701
702 tmpdir, err := os.MkdirTemp("", "cshared")
703 if err != nil {
704 t.Fatal(err)
705 }
706 defer os.RemoveAll(tmpdir)
707
708 copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "go.mod"), "go.mod")
709 copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "libgo", "libgo.go"), filepath.Join("libgo", "libgo.go"))
710 copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "p", "p.go"), filepath.Join("p", "p.go"))
711
712 buildcmd := []string{"go", "install", "-x", "-buildmode=c-shared", "-installsuffix", "testcshared", "./libgo"}
713
714 cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
715 cmd.Dir = filepath.Join(tmpdir, "src", "testcshared")
716 env := append(cmd.Environ(),
717 "GOPATH="+tmpdir,
718 "GOBIN="+filepath.Join(tmpdir, "bin"),
719 "GO111MODULE=off",
720 )
721 cmd.Env = env
722 t.Log(buildcmd)
723 out, err := cmd.CombinedOutput()
724 t.Logf("%s", out)
725 if err != nil {
726 t.Fatal(err)
727 }
728
729 var libgoh, ph string
730
731 walker := func(path string, info os.FileInfo, err error) error {
732 if err != nil {
733 t.Fatal(err)
734 }
735 var ps *string
736 switch filepath.Base(path) {
737 case "libgo.h":
738 ps = &libgoh
739 case "p.h":
740 ps = &ph
741 }
742 if ps != nil {
743 if *ps != "" {
744 t.Fatalf("%s found again", *ps)
745 }
746 *ps = path
747 }
748 return nil
749 }
750
751 if err := filepath.Walk(tmpdir, walker); err != nil {
752 t.Fatal(err)
753 }
754
755 if libgoh == "" {
756 t.Fatal("libgo.h not installed")
757 }
758
759 if err := os.Remove(libgoh); err != nil {
760 t.Fatal(err)
761 }
762
763 cmd = exec.Command(buildcmd[0], buildcmd[1:]...)
764 cmd.Dir = filepath.Join(tmpdir, "src", "testcshared")
765 cmd.Env = env
766 t.Log(buildcmd)
767 out, err = cmd.CombinedOutput()
768 t.Logf("%s", out)
769 if err != nil {
770 t.Fatal(err)
771 }
772
773 if _, err := os.Stat(libgoh); err != nil {
774 t.Errorf("libgo.h not installed in second run: %v", err)
775 }
776 }
777
778
779 func copyFile(t *testing.T, dst, src string) {
780 t.Helper()
781 data, err := os.ReadFile(src)
782 if err != nil {
783 t.Fatal(err)
784 }
785 if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
786 t.Fatal(err)
787 }
788 if err := os.WriteFile(dst, data, 0666); err != nil {
789 t.Fatal(err)
790 }
791 }
792
793 func TestGo2C2Go(t *testing.T) {
794 switch GOOS {
795 case "darwin", "ios", "windows":
796
797
798 t.Skipf("linking c-shared into Go programs not supported on %s; issue 29061, 49457", GOOS)
799 case "android":
800 t.Skip("test fails on android; issue 29087")
801 }
802 globalSkip(t)
803 testenv.MustHaveGoBuild(t)
804 testenv.MustHaveCGO(t)
805 testenv.MustHaveBuildMode(t, "c-shared")
806
807 t.Parallel()
808
809 tmpdir, err := os.MkdirTemp("", "cshared-TestGo2C2Go")
810 if err != nil {
811 t.Fatal(err)
812 }
813 defer os.RemoveAll(tmpdir)
814
815 lib := filepath.Join(tmpdir, "libtestgo2c2go.a")
816 var env []string
817 if GOOS == "windows" && strings.HasSuffix(lib, ".a") {
818 env = append(env, "CGO_LDFLAGS=-Wl,--out-implib,"+lib, "CGO_LDFLAGS_ALLOW=.*")
819 lib = strings.TrimSuffix(lib, ".a") + ".dll"
820 }
821 run(t, env, "go", "build", "-buildmode=c-shared", "-o", lib, "./go2c2go/go")
822
823 cgoCflags := os.Getenv("CGO_CFLAGS")
824 if cgoCflags != "" {
825 cgoCflags += " "
826 }
827 cgoCflags += "-I" + tmpdir
828
829 cgoLdflags := os.Getenv("CGO_LDFLAGS")
830 if cgoLdflags != "" {
831 cgoLdflags += " "
832 }
833 cgoLdflags += "-L" + tmpdir + " -ltestgo2c2go"
834
835 goenv := []string{"CGO_CFLAGS=" + cgoCflags, "CGO_LDFLAGS=" + cgoLdflags}
836
837 ldLibPath := os.Getenv("LD_LIBRARY_PATH")
838 if ldLibPath != "" {
839 ldLibPath += ":"
840 }
841 ldLibPath += tmpdir
842
843 runenv := []string{"LD_LIBRARY_PATH=" + ldLibPath}
844
845 bin := filepath.Join(tmpdir, "m1") + exeSuffix
846 run(t, goenv, "go", "build", "-o", bin, "./go2c2go/m1")
847 runExe(t, runenv, bin)
848
849 bin = filepath.Join(tmpdir, "m2") + exeSuffix
850 run(t, goenv, "go", "build", "-o", bin, "./go2c2go/m2")
851 runExe(t, runenv, bin)
852 }
853
854 func TestIssue36233(t *testing.T) {
855 globalSkip(t)
856 testenv.MustHaveCGO(t)
857
858 t.Parallel()
859
860
861
862
863 tmpdir, err := os.MkdirTemp("", "cshared-TestIssue36233")
864 if err != nil {
865 t.Fatal(err)
866 }
867 defer os.RemoveAll(tmpdir)
868
869 const exportHeader = "issue36233.h"
870
871 run(t, nil, "go", "tool", "cgo", "-exportheader", exportHeader, "-objdir", tmpdir, "./issue36233/issue36233.go")
872 data, err := os.ReadFile(exportHeader)
873 if err != nil {
874 t.Fatal(err)
875 }
876
877 funcs := []struct{ name, signature string }{
878 {"exportComplex64", "GoComplex64 exportComplex64(GoComplex64 v)"},
879 {"exportComplex128", "GoComplex128 exportComplex128(GoComplex128 v)"},
880 {"exportComplexfloat", "GoComplex64 exportComplexfloat(GoComplex64 v)"},
881 {"exportComplexdouble", "GoComplex128 exportComplexdouble(GoComplex128 v)"},
882 }
883
884 scanner := bufio.NewScanner(bytes.NewReader(data))
885 var found int
886 for scanner.Scan() {
887 b := scanner.Bytes()
888 for _, fn := range funcs {
889 if bytes.Contains(b, []byte(fn.name)) {
890 found++
891 if !bytes.Contains(b, []byte(fn.signature)) {
892 t.Errorf("function signature mismatch; got %q, want %q", b, fn.signature)
893 }
894 }
895 }
896 }
897 if err = scanner.Err(); err != nil {
898 t.Errorf("scanner encountered error: %v", err)
899 }
900 if found != len(funcs) {
901 t.Error("missing functions")
902 }
903 }
904
905 func TestIssue68411(t *testing.T) {
906 globalSkip(t)
907 testenv.MustHaveCGO(t)
908
909 t.Parallel()
910
911
912
913
914 tmpdir := t.TempDir()
915
916 const exportHeader = "issue68411.h"
917
918 run(t, nil, "go", "tool", "cgo", "-exportheader", exportHeader, "-objdir", tmpdir, "./issue68411/issue68411.go")
919 data, err := os.ReadFile(exportHeader)
920 if err != nil {
921 t.Fatal(err)
922 }
923
924 funcs := []struct{ name, signature string }{
925 {"exportFuncWithNoParams", "void exportFuncWithNoParams(void)"},
926 {"exportFuncWithParams", "exportFuncWithParams(GoInt a, GoInt b)"},
927 }
928
929 var found int
930 for line := range bytes.Lines(data) {
931 for _, fn := range funcs {
932 if bytes.Contains(line, []byte(fn.name)) {
933 found++
934 if !bytes.Contains(line, []byte(fn.signature)) {
935 t.Errorf("function signature mismatch; got %q, want %q", line, fn.signature)
936 }
937 }
938 }
939 }
940
941 if found != len(funcs) {
942 t.Error("missing functions")
943 }
944 }
945
View as plain text