diff options
Diffstat (limited to 'misc/cgo/testso')
-rw-r--r-- | misc/cgo/testso/noso_test.go | 10 | ||||
-rw-r--r-- | misc/cgo/testso/overlaydir_test.go | 78 | ||||
-rw-r--r-- | misc/cgo/testso/so_test.go | 142 | ||||
-rw-r--r-- | misc/cgo/testso/testdata/cgoso.c | 14 | ||||
-rw-r--r-- | misc/cgo/testso/testdata/cgoso.go | 32 | ||||
-rw-r--r-- | misc/cgo/testso/testdata/cgoso_c.c | 39 | ||||
-rw-r--r-- | misc/cgo/testso/testdata/cgoso_unix.go | 20 | ||||
-rw-r--r-- | misc/cgo/testso/testdata/main.go | 13 |
8 files changed, 348 insertions, 0 deletions
diff --git a/misc/cgo/testso/noso_test.go b/misc/cgo/testso/noso_test.go new file mode 100644 index 0000000..1014534 --- /dev/null +++ b/misc/cgo/testso/noso_test.go @@ -0,0 +1,10 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !cgo +// +build !cgo + +package so_test + +// Nothing to test. diff --git a/misc/cgo/testso/overlaydir_test.go b/misc/cgo/testso/overlaydir_test.go new file mode 100644 index 0000000..09a1d51 --- /dev/null +++ b/misc/cgo/testso/overlaydir_test.go @@ -0,0 +1,78 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package so_test + +import ( + "io" + "os" + "path/filepath" + "strings" +) + +// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added. +// +// TODO: Once we no longer need to support the misc module in GOPATH mode, +// factor this function out into a package to reduce duplication. +func overlayDir(dstRoot, srcRoot string) error { + dstRoot = filepath.Clean(dstRoot) + if err := os.MkdirAll(dstRoot, 0777); err != nil { + return err + } + + srcRoot, err := filepath.Abs(srcRoot) + if err != nil { + return err + } + + return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error { + if err != nil || srcPath == srcRoot { + return err + } + + suffix := strings.TrimPrefix(srcPath, srcRoot) + for len(suffix) > 0 && suffix[0] == filepath.Separator { + suffix = suffix[1:] + } + dstPath := filepath.Join(dstRoot, suffix) + + perm := info.Mode() & os.ModePerm + if info.Mode()&os.ModeSymlink != 0 { + info, err = os.Stat(srcPath) + if err != nil { + return err + } + perm = info.Mode() & os.ModePerm + } + + // Always copy directories (don't symlink them). + // If we add a file in the overlay, we don't want to add it in the original. + if info.IsDir() { + return os.MkdirAll(dstPath, perm|0200) + } + + // If the OS supports symlinks, use them instead of copying bytes. + if err := os.Symlink(srcPath, dstPath); err == nil { + return nil + } + + // Otherwise, copy the bytes. + src, err := os.Open(srcPath) + if err != nil { + return err + } + defer src.Close() + + dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) + if err != nil { + return err + } + + _, err = io.Copy(dst, src) + if closeErr := dst.Close(); err == nil { + err = closeErr + } + return err + }) +} diff --git a/misc/cgo/testso/so_test.go b/misc/cgo/testso/so_test.go new file mode 100644 index 0000000..6d14e32 --- /dev/null +++ b/misc/cgo/testso/so_test.go @@ -0,0 +1,142 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build cgo +// +build cgo + +package so_test + +import ( + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strings" + "testing" +) + +func requireTestSOSupported(t *testing.T) { + t.Helper() + switch runtime.GOARCH { + case "arm64": + if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { + t.Skip("No exec facility on iOS.") + } + case "ppc64": + if runtime.GOOS == "linux" { + t.Skip("External linking not implemented on linux/ppc64 (issue #8912).") + } + } + if runtime.GOOS == "android" { + t.Skip("No exec facility on Android.") + } +} + +func TestSO(t *testing.T) { + requireTestSOSupported(t) + + GOPATH, err := os.MkdirTemp("", "cgosotest") + if err != nil { + log.Fatal(err) + } + defer os.RemoveAll(GOPATH) + + modRoot := filepath.Join(GOPATH, "src", "cgosotest") + if err := overlayDir(modRoot, "testdata"); err != nil { + log.Panic(err) + } + if err := os.WriteFile(filepath.Join(modRoot, "go.mod"), []byte("module cgosotest\n"), 0666); err != nil { + log.Panic(err) + } + + cmd := exec.Command("go", "env", "CC", "GOGCCFLAGS") + cmd.Dir = modRoot + cmd.Stderr = new(strings.Builder) + cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) + out, err := cmd.Output() + if err != nil { + t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr) + } + lines := strings.Split(string(out), "\n") + if len(lines) != 3 || lines[2] != "" { + t.Fatalf("Unexpected output from %s:\n%s", strings.Join(cmd.Args, " "), lines) + } + + cc := lines[0] + if cc == "" { + t.Fatal("CC environment variable (go env CC) cannot be empty") + } + gogccflags := strings.Split(lines[1], " ") + + // build shared object + ext := "so" + args := append(gogccflags, "-shared") + switch runtime.GOOS { + case "darwin", "ios": + ext = "dylib" + args = append(args, "-undefined", "suppress", "-flat_namespace") + case "windows": + ext = "dll" + args = append(args, "-DEXPORT_DLL") + // At least in mingw-clang it is not permitted to just name a .dll + // on the command line. You must name the corresponding import + // library instead, even though the dll is used when the executable is run. + args = append(args, "-Wl,-out-implib,libcgosotest.a") + case "aix": + ext = "so.1" + } + sofname := "libcgosotest." + ext + args = append(args, "-o", sofname, "cgoso_c.c") + + cmd = exec.Command(cc, args...) + cmd.Dir = modRoot + cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out) + + if runtime.GOOS == "aix" { + // Shared object must be wrapped by an archive + cmd = exec.Command("ar", "-X64", "-q", "libcgosotest.a", "libcgosotest.so.1") + cmd.Dir = modRoot + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + } + + cmd = exec.Command("go", "build", "-o", "main.exe", "main.go") + cmd.Dir = modRoot + cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out) + + cmd = exec.Command("./main.exe") + cmd.Dir = modRoot + cmd.Env = append(os.Environ(), "GOPATH="+GOPATH) + if runtime.GOOS != "windows" { + s := "LD_LIBRARY_PATH" + if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { + s = "DYLD_LIBRARY_PATH" + } + cmd.Env = append(os.Environ(), s+"=.") + + // On FreeBSD 64-bit architectures, the 32-bit linker looks for + // different environment variables. + if runtime.GOOS == "freebsd" && runtime.GOARCH == "386" { + cmd.Env = append(cmd.Env, "LD_32_LIBRARY_PATH=.") + } + } + out, err = cmd.CombinedOutput() + if err != nil { + t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out) + } + t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out) +} diff --git a/misc/cgo/testso/testdata/cgoso.c b/misc/cgo/testso/testdata/cgoso.c new file mode 100644 index 0000000..612e5d3 --- /dev/null +++ b/misc/cgo/testso/testdata/cgoso.c @@ -0,0 +1,14 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "_cgo_export.h" + +#if defined(WIN32) || defined(_AIX) +extern void setCallback(void *); +void init() { + setCallback(goCallback); +} +#else +void init() {} +#endif diff --git a/misc/cgo/testso/testdata/cgoso.go b/misc/cgo/testso/testdata/cgoso.go new file mode 100644 index 0000000..b59b2a8 --- /dev/null +++ b/misc/cgo/testso/testdata/cgoso.go @@ -0,0 +1,32 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgosotest + +/* +// intentionally write the same LDFLAGS differently +// to test correct handling of LDFLAGS. +#cgo linux LDFLAGS: -L. -lcgosotest +#cgo dragonfly LDFLAGS: -L. -l cgosotest +#cgo freebsd LDFLAGS: -L. -l cgosotest +#cgo openbsd LDFLAGS: -L. -l cgosotest +#cgo solaris LDFLAGS: -L. -lcgosotest +#cgo netbsd LDFLAGS: -L. libcgosotest.so +#cgo darwin LDFLAGS: -L. libcgosotest.dylib +#cgo windows LDFLAGS: -L. libcgosotest.a +#cgo aix LDFLAGS: -L. -l cgosotest + +void init(void); +void sofunc(void); +*/ +import "C" + +func Test() { + C.init() + C.sofunc() +} + +//export goCallback +func goCallback() { +} diff --git a/misc/cgo/testso/testdata/cgoso_c.c b/misc/cgo/testso/testdata/cgoso_c.c new file mode 100644 index 0000000..e5015ed --- /dev/null +++ b/misc/cgo/testso/testdata/cgoso_c.c @@ -0,0 +1,39 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +#ifdef WIN32 +// A Windows DLL is unable to call an arbitrary function in +// the main executable. Work around that by making the main +// executable pass the callback function pointer to us. +void (*goCallback)(void); +__declspec(dllexport) void setCallback(void *f) +{ + goCallback = (void (*)())f; +} +__declspec(dllexport) void sofunc(void); +#elif defined(_AIX) +// AIX doesn't allow the creation of a shared object with an +// undefined symbol. It's possible to bypass this problem by +// using -Wl,-G and -Wl,-brtl option which allows run-time linking. +// However, that's not how most of AIX shared object works. +// Therefore, it's better to consider goCallback as a pointer and +// to set up during an init function. +void (*goCallback)(void); +void setCallback(void *f) { goCallback = f; } +#else +extern void goCallback(void); +void setCallback(void *f) { (void)f; } +#endif + +// OpenBSD and older Darwin lack TLS support +#if !defined(__OpenBSD__) && !defined(__APPLE__) +__thread int tlsvar = 12345; +#endif + +void sofunc(void) +{ + goCallback(); +} diff --git a/misc/cgo/testso/testdata/cgoso_unix.go b/misc/cgo/testso/testdata/cgoso_unix.go new file mode 100644 index 0000000..1860694 --- /dev/null +++ b/misc/cgo/testso/testdata/cgoso_unix.go @@ -0,0 +1,20 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build aix dragonfly freebsd linux netbsd solaris + +package cgosotest + +/* +extern int __thread tlsvar; +int *getTLS() { return &tlsvar; } +*/ +import "C" + +func init() { + if v := *C.getTLS(); v != 12345 { + println("got", v) + panic("BAD TLS value") + } +} diff --git a/misc/cgo/testso/testdata/main.go b/misc/cgo/testso/testdata/main.go new file mode 100644 index 0000000..963d451 --- /dev/null +++ b/misc/cgo/testso/testdata/main.go @@ -0,0 +1,13 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +package main + +import "cgosotest" + +func main() { + cgosotest.Test() +} |