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