diff options
author | Ian Lance Taylor <iant@golang.org> | 2018-01-09 01:23:08 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2018-01-09 01:23:08 +0000 |
commit | 1a2f01efa63036a5104f203a4789e682c0e0915d (patch) | |
tree | 373e15778dc8295354584e1f86915ae493b604ff /libgo/misc | |
parent | 8799df67f2dab88f9fda11739c501780a85575e2 (diff) |
libgo: update to Go1.10beta1
Update the Go library to the 1.10beta1 release.
Requires a few changes to the compiler for modifications to the map
runtime code, and to handle some nowritebarrier cases in the runtime.
Reviewed-on: https://go-review.googlesource.com/86455
gotools/:
* Makefile.am (go_cmd_vet_files): New variable.
(go_cmd_buildid_files, go_cmd_test2json_files): New variables.
(s-zdefaultcc): Change from constants to functions.
(noinst_PROGRAMS): Add vet, buildid, and test2json.
(cgo$(EXEEXT)): Link against $(LIBGOTOOL).
(vet$(EXEEXT)): New target.
(buildid$(EXEEXT)): New target.
(test2json$(EXEEXT)): New target.
(install-exec-local): Install all $(noinst_PROGRAMS).
(uninstall-local): Uninstasll all $(noinst_PROGRAMS).
(check-go-tool): Depend on $(noinst_PROGRAMS). Copy down
objabi.go.
(check-runtime): Depend on $(noinst_PROGRAMS).
(check-cgo-test, check-carchive-test): Likewise.
(check-vet): New target.
(check): Depend on check-vet. Look at cmd_vet-testlog.
(.PHONY): Add check-vet.
* Makefile.in: Rebuild.
From-SVN: r256365
Diffstat (limited to 'libgo/misc')
93 files changed, 2306 insertions, 739 deletions
diff --git a/libgo/misc/cgo/errors/errors_test.go b/libgo/misc/cgo/errors/errors_test.go new file mode 100644 index 00000000000..118187f23b8 --- /dev/null +++ b/libgo/misc/cgo/errors/errors_test.go @@ -0,0 +1,161 @@ +// Copyright 2017 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 errorstest + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "strconv" + "strings" + "testing" +) + +func path(file string) string { + return filepath.Join("src", file) +} + +func check(t *testing.T, file string) { + t.Run(file, func(t *testing.T) { + t.Parallel() + + contents, err := ioutil.ReadFile(path(file)) + if err != nil { + t.Fatal(err) + } + var errors []*regexp.Regexp + for i, line := range bytes.Split(contents, []byte("\n")) { + if bytes.HasSuffix(line, []byte("ERROR HERE")) { + re := regexp.MustCompile(regexp.QuoteMeta(fmt.Sprintf("%s:%d:", file, i+1))) + errors = append(errors, re) + continue + } + + frags := bytes.SplitAfterN(line, []byte("ERROR HERE: "), 2) + if len(frags) == 1 { + continue + } + re, err := regexp.Compile(string(frags[1])) + if err != nil { + t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frags[1]) + continue + } + errors = append(errors, re) + } + if len(errors) == 0 { + t.Fatalf("cannot find ERROR HERE") + } + expect(t, file, errors) + }) +} + +func expect(t *testing.T, file string, errors []*regexp.Regexp) { + dir, err := ioutil.TempDir("", filepath.Base(t.Name())) + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + dst := filepath.Join(dir, strings.TrimSuffix(file, ".go")) + cmd := exec.Command("go", "build", "-gcflags=-L", "-o="+dst, path(file)) // TODO(gri) no need for -gcflags=-L if go tool is adjusted + out, err := cmd.CombinedOutput() + if err == nil { + t.Errorf("expected cgo to fail but it succeeded") + } + + lines := bytes.Split(out, []byte("\n")) + for _, re := range errors { + found := false + for _, line := range lines { + if re.Match(line) { + t.Logf("found match for %#q: %q", re, line) + found = true + break + } + } + if !found { + t.Errorf("expected error output to contain %#q", re) + } + } + + if t.Failed() { + t.Logf("actual output:\n%s", out) + } +} + +func sizeofLongDouble(t *testing.T) int { + cmd := exec.Command("go", "run", path("long_double_size.go")) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out) + } + + i, err := strconv.Atoi(strings.TrimSpace(string(out))) + if err != nil { + t.Fatalf("long_double_size.go printed invalid size: %s", out) + } + return i +} + +func TestReportsTypeErrors(t *testing.T) { + for _, file := range []string{ + "err1.go", + "err2.go", + "err3.go", + "issue7757.go", + "issue8442.go", + "issue11097a.go", + "issue11097b.go", + "issue13129.go", + "issue13423.go", + "issue13467.go", + "issue13635.go", + "issue13830.go", + "issue16116.go", + "issue16591.go", + "issue18452.go", + "issue18889.go", + } { + check(t, file) + } + + if sizeofLongDouble(t) > 8 { + check(t, "err4.go") + } +} + +func TestToleratesOptimizationFlag(t *testing.T) { + for _, cflags := range []string{ + "", + "-O", + } { + cflags := cflags + t.Run(cflags, func(t *testing.T) { + t.Parallel() + + cmd := exec.Command("go", "build", path("issue14669.go")) + cmd.Env = append(os.Environ(), "CGO_CFLAGS="+cflags) + out, err := cmd.CombinedOutput() + if err != nil { + t.Errorf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out) + } + }) + } +} + +func TestMallocCrashesOnNil(t *testing.T) { + t.Parallel() + + cmd := exec.Command("go", "run", path("malloc.go")) + out, err := cmd.CombinedOutput() + if err == nil { + t.Logf("%#q:\n%s", strings.Join(cmd.Args, " "), out) + t.Fatalf("succeeded unexpectedly") + } +} diff --git a/libgo/misc/cgo/errors/issue13635.go b/libgo/misc/cgo/errors/issue13635.go deleted file mode 100644 index 0ce2b1e83a1..00000000000 --- a/libgo/misc/cgo/errors/issue13635.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015 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. - -// issue 13635: used to output error about C.unsignedchar. -// This test tests all such types. - -package pkg - -import "C" - -func main() { - var ( - _ C.uchar = "uc" // ERROR HERE - _ C.schar = "sc" // ERROR HERE - _ C.ushort = "us" // ERROR HERE - _ C.uint = "ui" // ERROR HERE - _ C.ulong = "ul" // ERROR HERE - _ C.longlong = "ll" // ERROR HERE - _ C.ulonglong = "ull" // ERROR HERE - _ C.complexfloat = "cf" // ERROR HERE - _ C.complexdouble = "cd" // ERROR HERE - ) -} diff --git a/libgo/misc/cgo/errors/ptr.go b/libgo/misc/cgo/errors/ptr_test.go index 3e117666bff..d295a5849db 100644 --- a/libgo/misc/cgo/errors/ptr.go +++ b/libgo/misc/cgo/errors/ptr_test.go @@ -4,20 +4,18 @@ // Tests that cgo detects invalid pointer passing at runtime. -package main +package errorstest import ( "bufio" "bytes" "fmt" - "io" "io/ioutil" "os" "os/exec" "path/filepath" - "runtime" "strings" - "sync" + "testing" ) // ptrTest is the tests without the boilerplate. @@ -344,7 +342,7 @@ var ptrTests = []ptrTest{ fail: false, }, { - // Issue #21306. + // Test preemption while entering a cgo call. Issue #21306. name: "preempt-during-call", c: `void f() {}`, imports: []string{"runtime", "sync"}, @@ -353,219 +351,145 @@ var ptrTests = []ptrTest{ }, } -func main() { - os.Exit(doTests()) +func TestPointerChecks(t *testing.T) { + for _, pt := range ptrTests { + pt := pt + t.Run(pt.name, func(t *testing.T) { + testOne(t, pt) + }) + } } -func doTests() int { - gopath, err := ioutil.TempDir("", "cgoerrors") +func testOne(t *testing.T, pt ptrTest) { + t.Parallel() + + gopath, err := ioutil.TempDir("", filepath.Base(t.Name())) if err != nil { - fmt.Fprintln(os.Stderr, err) - return 2 + t.Fatal(err) } defer os.RemoveAll(gopath) - if err := os.MkdirAll(filepath.Join(gopath, "src"), 0777); err != nil { - fmt.Fprintln(os.Stderr, err) - return 2 - } - - workers := runtime.NumCPU() + 1 - - var wg sync.WaitGroup - c := make(chan int) - errs := make(chan int) - for i := 0; i < workers; i++ { - wg.Add(1) - go func() { - worker(gopath, c, errs) - wg.Done() - }() - } - - for i := range ptrTests { - c <- i - } - close(c) - - go func() { - wg.Wait() - close(errs) - }() - - tot := 0 - for e := range errs { - tot += e - } - return tot -} - -func worker(gopath string, c, errs chan int) { - e := 0 - for i := range c { - if !doOne(gopath, i) { - e++ - } - } - if e > 0 { - errs <- e - } -} - -func doOne(gopath string, i int) bool { - t := &ptrTests[i] - - dir := filepath.Join(gopath, "src", fmt.Sprintf("dir%d", i)) - if err := os.Mkdir(dir, 0777); err != nil { - fmt.Fprintln(os.Stderr, err) - return false + src := filepath.Join(gopath, "src") + if err := os.Mkdir(src, 0777); err != nil { + t.Fatal(err) } - name := filepath.Join(dir, fmt.Sprintf("t%d.go", i)) + name := filepath.Join(src, fmt.Sprintf("%s.go", filepath.Base(t.Name()))) f, err := os.Create(name) if err != nil { - fmt.Fprintln(os.Stderr, err) - return false + t.Fatal(err) } b := bufio.NewWriter(f) fmt.Fprintln(b, `package main`) fmt.Fprintln(b) fmt.Fprintln(b, `/*`) - fmt.Fprintln(b, t.c) + fmt.Fprintln(b, pt.c) fmt.Fprintln(b, `*/`) fmt.Fprintln(b, `import "C"`) fmt.Fprintln(b) - for _, imp := range t.imports { + for _, imp := range pt.imports { fmt.Fprintln(b, `import "`+imp+`"`) } - if len(t.imports) > 0 { + if len(pt.imports) > 0 { fmt.Fprintln(b) } - if len(t.support) > 0 { - fmt.Fprintln(b, t.support) + if len(pt.support) > 0 { + fmt.Fprintln(b, pt.support) fmt.Fprintln(b) } fmt.Fprintln(b, `func main() {`) - fmt.Fprintln(b, t.body) + fmt.Fprintln(b, pt.body) fmt.Fprintln(b, `}`) if err := b.Flush(); err != nil { - fmt.Fprintf(os.Stderr, "flushing %s: %v\n", name, err) - return false + t.Fatalf("flushing %s: %v", name, err) } if err := f.Close(); err != nil { - fmt.Fprintf(os.Stderr, "closing %s: %v\n", name, err) - return false + t.Fatalf("closing %s: %v", name, err) } - for _, e := range t.extra { - if err := ioutil.WriteFile(filepath.Join(dir, e.name), []byte(e.contents), 0644); err != nil { - fmt.Fprintf(os.Stderr, "writing %s: %v\n", e.name, err) - return false + for _, e := range pt.extra { + if err := ioutil.WriteFile(filepath.Join(src, e.name), []byte(e.contents), 0644); err != nil { + t.Fatalf("writing %s: %v", e.name, err) } } - ok := true + args := func(cmd *exec.Cmd) string { + return strings.Join(cmd.Args, " ") + } cmd := exec.Command("go", "build") - cmd.Dir = dir + cmd.Dir = src cmd.Env = addEnv("GOPATH", gopath) buf, err := cmd.CombinedOutput() if err != nil { - fmt.Fprintf(os.Stderr, "test %s failed to build: %v\n%s", t.name, err, buf) - return false + t.Logf("%#q:\n%s", args(cmd), buf) + t.Fatalf("failed to build: %v", err) } - exe := filepath.Join(dir, filepath.Base(dir)) + exe := filepath.Join(src, filepath.Base(src)) cmd = exec.Command(exe) - cmd.Dir = dir + cmd.Dir = src - if t.expensive { + if pt.expensive { cmd.Env = cgocheckEnv("1") buf, err := cmd.CombinedOutput() if err != nil { - var errbuf bytes.Buffer - if t.fail { - fmt.Fprintf(&errbuf, "test %s marked expensive but failed when not expensive: %v\n", t.name, err) + t.Logf("%#q:\n%s", args(cmd), buf) + if pt.fail { + t.Fatalf("test marked expensive, but failed when not expensive: %v", err) } else { - fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=1: %v\n", t.name, err) + t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err) } - reportTestOutput(&errbuf, t.name, buf) - os.Stderr.Write(errbuf.Bytes()) - ok = false } cmd = exec.Command(exe) - cmd.Dir = dir + cmd.Dir = src } - if t.expensive { + if pt.expensive { cmd.Env = cgocheckEnv("2") } buf, err = cmd.CombinedOutput() - - if t.fail { + if pt.fail { if err == nil { - var errbuf bytes.Buffer - fmt.Fprintf(&errbuf, "test %s did not fail as expected\n", t.name) - reportTestOutput(&errbuf, t.name, buf) - os.Stderr.Write(errbuf.Bytes()) - ok = false + t.Logf("%#q:\n%s", args(cmd), buf) + t.Fatalf("did not fail as expected") } else if !bytes.Contains(buf, []byte("Go pointer")) { - var errbuf bytes.Buffer - fmt.Fprintf(&errbuf, "test %s output does not contain expected error (failed with %v)\n", t.name, err) - reportTestOutput(&errbuf, t.name, buf) - os.Stderr.Write(errbuf.Bytes()) - ok = false + t.Logf("%#q:\n%s", args(cmd), buf) + t.Fatalf("did not print expected error (failed with %v)", err) } } else { if err != nil { - var errbuf bytes.Buffer - fmt.Fprintf(&errbuf, "test %s failed unexpectedly: %v\n", t.name, err) - reportTestOutput(&errbuf, t.name, buf) - os.Stderr.Write(errbuf.Bytes()) - ok = false + t.Logf("%#q:\n%s", args(cmd), buf) + t.Fatalf("failed unexpectedly: %v", err) } - if !t.expensive && ok { + if !pt.expensive { // Make sure it passes with the expensive checks. cmd := exec.Command(exe) - cmd.Dir = dir + cmd.Dir = src cmd.Env = cgocheckEnv("2") buf, err := cmd.CombinedOutput() if err != nil { - var errbuf bytes.Buffer - fmt.Fprintf(&errbuf, "test %s failed unexpectedly with expensive checks: %v\n", t.name, err) - reportTestOutput(&errbuf, t.name, buf) - os.Stderr.Write(errbuf.Bytes()) - ok = false + t.Logf("%#q:\n%s", args(cmd), buf) + t.Fatalf("failed unexpectedly with expensive checks: %v", err) } } } - if t.fail && ok { + if pt.fail { cmd = exec.Command(exe) - cmd.Dir = dir + cmd.Dir = src cmd.Env = cgocheckEnv("0") buf, err := cmd.CombinedOutput() if err != nil { - var errbuf bytes.Buffer - fmt.Fprintf(&errbuf, "test %s failed unexpectedly with GODEBUG=cgocheck=0: %v\n", t.name, err) - reportTestOutput(&errbuf, t.name, buf) - os.Stderr.Write(errbuf.Bytes()) - ok = false + t.Logf("%#q:\n%s", args(cmd), buf) + t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err) } } - - return ok -} - -func reportTestOutput(w io.Writer, name string, buf []byte) { - fmt.Fprintf(w, "=== test %s output ===\n", name) - fmt.Fprintf(w, "%s", buf) - fmt.Fprintf(w, "=== end of test %s output ===\n", name) } func cgocheckEnv(val string) []string { diff --git a/libgo/misc/cgo/errors/err1.go b/libgo/misc/cgo/errors/src/err1.go index 61bbcd29577..61bbcd29577 100644 --- a/libgo/misc/cgo/errors/err1.go +++ b/libgo/misc/cgo/errors/src/err1.go diff --git a/libgo/misc/cgo/errors/err2.go b/libgo/misc/cgo/errors/src/err2.go index 3ab410bbaac..3ab410bbaac 100644 --- a/libgo/misc/cgo/errors/err2.go +++ b/libgo/misc/cgo/errors/src/err2.go diff --git a/libgo/misc/cgo/errors/err3.go b/libgo/misc/cgo/errors/src/err3.go index 609e1a0b748..609e1a0b748 100644 --- a/libgo/misc/cgo/errors/err3.go +++ b/libgo/misc/cgo/errors/src/err3.go diff --git a/libgo/misc/cgo/errors/src/err4.go b/libgo/misc/cgo/errors/src/err4.go new file mode 100644 index 00000000000..8e5f78e987b --- /dev/null +++ b/libgo/misc/cgo/errors/src/err4.go @@ -0,0 +1,15 @@ +// Copyright 2017 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 main + +/* +long double x = 0; +*/ +import "C" + +func main() { + _ = C.x // ERROR HERE + _ = C.x +} diff --git a/libgo/misc/cgo/errors/issue11097a.go b/libgo/misc/cgo/errors/src/issue11097a.go index 028d10ce5cb..028d10ce5cb 100644 --- a/libgo/misc/cgo/errors/issue11097a.go +++ b/libgo/misc/cgo/errors/src/issue11097a.go diff --git a/libgo/misc/cgo/errors/issue11097b.go b/libgo/misc/cgo/errors/src/issue11097b.go index b00f24fc103..b00f24fc103 100644 --- a/libgo/misc/cgo/errors/issue11097b.go +++ b/libgo/misc/cgo/errors/src/issue11097b.go diff --git a/libgo/misc/cgo/errors/issue13129.go b/libgo/misc/cgo/errors/src/issue13129.go index f7ad7a7e149..057bce4b829 100644 --- a/libgo/misc/cgo/errors/issue13129.go +++ b/libgo/misc/cgo/errors/src/issue13129.go @@ -10,5 +10,5 @@ import "C" func main() { var x C.ushort - x = int(0) // ERROR HERE + x = int(0) // ERROR HERE: C\.ushort } diff --git a/libgo/misc/cgo/errors/issue13423.go b/libgo/misc/cgo/errors/src/issue13423.go index fc191572376..fc191572376 100644 --- a/libgo/misc/cgo/errors/issue13423.go +++ b/libgo/misc/cgo/errors/src/issue13423.go diff --git a/libgo/misc/cgo/errors/src/issue13467.go b/libgo/misc/cgo/errors/src/issue13467.go new file mode 100644 index 00000000000..e061880ddab --- /dev/null +++ b/libgo/misc/cgo/errors/src/issue13467.go @@ -0,0 +1,15 @@ +// Copyright 2017 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 p + +/* +static int transform(int x) { return x; } +*/ +import "C" + +func F() { + var x rune = '✈' + var _ rune = C.transform(x) // ERROR HERE: C\.int +} diff --git a/libgo/misc/cgo/errors/src/issue13635.go b/libgo/misc/cgo/errors/src/issue13635.go new file mode 100644 index 00000000000..3f38f5df4b5 --- /dev/null +++ b/libgo/misc/cgo/errors/src/issue13635.go @@ -0,0 +1,24 @@ +// Copyright 2015 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. + +// issue 13635: used to output error about C.unsignedchar. +// This test tests all such types. + +package pkg + +import "C" + +func main() { + var ( + _ C.uchar = "uc" // ERROR HERE: C\.uchar + _ C.schar = "sc" // ERROR HERE: C\.schar + _ C.ushort = "us" // ERROR HERE: C\.ushort + _ C.uint = "ui" // ERROR HERE: C\.uint + _ C.ulong = "ul" // ERROR HERE: C\.ulong + _ C.longlong = "ll" // ERROR HERE: C\.longlong + _ C.ulonglong = "ull" // ERROR HERE: C\.ulonglong + _ C.complexfloat = "cf" // ERROR HERE: C\.complexfloat + _ C.complexdouble = "cd" // ERROR HERE: C\.complexdouble + ) +} diff --git a/libgo/misc/cgo/errors/issue13830.go b/libgo/misc/cgo/errors/src/issue13830.go index ac20c82b81b..ac20c82b81b 100644 --- a/libgo/misc/cgo/errors/issue13830.go +++ b/libgo/misc/cgo/errors/src/issue13830.go diff --git a/libgo/misc/cgo/errors/issue14669.go b/libgo/misc/cgo/errors/src/issue14669.go index 04d2bcb631d..04d2bcb631d 100644 --- a/libgo/misc/cgo/errors/issue14669.go +++ b/libgo/misc/cgo/errors/src/issue14669.go diff --git a/libgo/misc/cgo/errors/issue16116.go b/libgo/misc/cgo/errors/src/issue16116.go index 1e01cab844e..1e01cab844e 100644 --- a/libgo/misc/cgo/errors/issue16116.go +++ b/libgo/misc/cgo/errors/src/issue16116.go diff --git a/libgo/misc/cgo/errors/issue16591.go b/libgo/misc/cgo/errors/src/issue16591.go index 10eb8403cf8..10eb8403cf8 100644 --- a/libgo/misc/cgo/errors/issue16591.go +++ b/libgo/misc/cgo/errors/src/issue16591.go diff --git a/libgo/misc/cgo/errors/issue18452.go b/libgo/misc/cgo/errors/src/issue18452.go index 36ef7f54e12..0386d768927 100644 --- a/libgo/misc/cgo/errors/issue18452.go +++ b/libgo/misc/cgo/errors/src/issue18452.go @@ -13,6 +13,6 @@ import ( func a() { fmt.Println("Hello, world!") - C.function_that_does_not_exist() // line 16 - C.pi // line 17 + C.function_that_does_not_exist() // ERROR HERE + C.pi // ERROR HERE } diff --git a/libgo/misc/cgo/errors/issue18889.go b/libgo/misc/cgo/errors/src/issue18889.go index bba6b8f9bb1..bba6b8f9bb1 100644 --- a/libgo/misc/cgo/errors/issue18889.go +++ b/libgo/misc/cgo/errors/src/issue18889.go diff --git a/libgo/misc/cgo/errors/issue7757.go b/libgo/misc/cgo/errors/src/issue7757.go index 0426e9fb7ef..0426e9fb7ef 100644 --- a/libgo/misc/cgo/errors/issue7757.go +++ b/libgo/misc/cgo/errors/src/issue7757.go diff --git a/libgo/misc/cgo/errors/issue8442.go b/libgo/misc/cgo/errors/src/issue8442.go index 60477ad345e..60477ad345e 100644 --- a/libgo/misc/cgo/errors/issue8442.go +++ b/libgo/misc/cgo/errors/src/issue8442.go diff --git a/libgo/misc/cgo/errors/src/long_double_size.go b/libgo/misc/cgo/errors/src/long_double_size.go new file mode 100644 index 00000000000..8b797f886ae --- /dev/null +++ b/libgo/misc/cgo/errors/src/long_double_size.go @@ -0,0 +1,16 @@ +// Copyright 2017 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 main + +/* +const int sizeofLongDouble = sizeof(long double); +*/ +import "C" + +import "fmt" + +func main() { + fmt.Println(C.sizeofLongDouble) +} diff --git a/libgo/misc/cgo/errors/malloc.go b/libgo/misc/cgo/errors/src/malloc.go index 65da0208b97..65da0208b97 100644 --- a/libgo/misc/cgo/errors/malloc.go +++ b/libgo/misc/cgo/errors/src/malloc.go diff --git a/libgo/misc/cgo/errors/test.bash b/libgo/misc/cgo/errors/test.bash deleted file mode 100644 index ed0b0946925..00000000000 --- a/libgo/misc/cgo/errors/test.bash +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash - -# 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. - -check() { - file=$1 - line=$(grep -n 'ERROR HERE' $file | sed 's/:.*//') - if [ "$line" = "" ]; then - echo 1>&2 misc/cgo/errors/test.bash: BUG: cannot find ERROR HERE in $file - exit 1 - fi - expect $file $file:$line: -} - -expect() { - file=$1 - shift - if go build -gcflags=-C $file >errs 2>&1; then - echo 1>&2 misc/cgo/errors/test.bash: BUG: expected cgo to fail on $file but it succeeded - exit 1 - fi - if ! test -s errs; then - echo 1>&2 misc/cgo/errors/test.bash: BUG: expected error output for $file but saw none - exit 1 - fi - for error; do - if ! fgrep $error errs >/dev/null 2>&1; then - echo 1>&2 misc/cgo/errors/test.bash: BUG: expected error output for $file to contain \"$error\" but saw: - cat 1>&2 errs - exit 1 - fi - done -} - -check err1.go -check err2.go -check err3.go -check issue7757.go -check issue8442.go -check issue11097a.go -check issue11097b.go -expect issue13129.go C.ushort -check issue13423.go -expect issue13635.go C.uchar C.schar C.ushort C.uint C.ulong C.longlong C.ulonglong C.complexfloat C.complexdouble -check issue13830.go -check issue16116.go -check issue16591.go -check issue18889.go -expect issue18452.go issue18452.go:16 issue18452.go:17 - -if ! go build issue14669.go; then - exit 1 -fi -if ! CGO_CFLAGS="-O" go build issue14669.go; then - exit 1 -fi - -if ! go run ptr.go; then - exit 1 -fi - -# The malloc.go test should crash. -rm -f malloc.out -if go run malloc.go >malloc.out 2>&1; then - echo '`go run malloc.go` succeeded unexpectedly' - cat malloc.out - rm -f malloc.out - exit 1 -fi -rm -f malloc.out - -rm -rf errs _obj -exit 0 diff --git a/libgo/misc/cgo/life/main.go b/libgo/misc/cgo/life/main.go index aa2f6d116b3..45376fd05a9 100644 --- a/libgo/misc/cgo/life/main.go +++ b/libgo/misc/cgo/life/main.go @@ -1,4 +1,4 @@ -// cmpout +// cmpout -tags=use_go_run // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -11,9 +11,10 @@ package main import ( - "." "flag" "fmt" + + "." ) const MAXDIM = 100 diff --git a/libgo/misc/cgo/stdio/chain.go b/libgo/misc/cgo/stdio/chain.go index 03cddb76888..0fa813cab70 100644 --- a/libgo/misc/cgo/stdio/chain.go +++ b/libgo/misc/cgo/stdio/chain.go @@ -1,4 +1,4 @@ -// cmpout +// cmpout -tags=use_go_run // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/libgo/misc/cgo/stdio/fib.go b/libgo/misc/cgo/stdio/fib.go index 61a1b83728c..56e32552ee6 100644 --- a/libgo/misc/cgo/stdio/fib.go +++ b/libgo/misc/cgo/stdio/fib.go @@ -1,4 +1,4 @@ -// cmpout +// cmpout -tags=use_go_run // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/libgo/misc/cgo/stdio/hello.go b/libgo/misc/cgo/stdio/hello.go index 47179ba4827..63bff4c617a 100644 --- a/libgo/misc/cgo/stdio/hello.go +++ b/libgo/misc/cgo/stdio/hello.go @@ -1,4 +1,4 @@ -// cmpout +// cmpout -tags=use_go_run // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style diff --git a/libgo/misc/cgo/test/cgo_test.go b/libgo/misc/cgo/test/cgo_test.go index f7cf6f613c4..67abfff2c03 100644 --- a/libgo/misc/cgo/test/cgo_test.go +++ b/libgo/misc/cgo/test/cgo_test.go @@ -80,5 +80,11 @@ func Test20369(t *testing.T) { test20369(t) } func Test18720(t *testing.T) { test18720(t) } func Test20266(t *testing.T) { test20266(t) } func Test20129(t *testing.T) { test20129(t) } +func Test20910(t *testing.T) { test20910(t) } +func Test21708(t *testing.T) { test21708(t) } +func Test21809(t *testing.T) { test21809(t) } +func Test6907(t *testing.T) { test6907(t) } +func Test6907Go(t *testing.T) { test6907Go(t) } +func Test21897(t *testing.T) { test21897(t) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } diff --git a/libgo/misc/cgo/test/issue18720.go b/libgo/misc/cgo/test/issue18720.go index a93304498e0..3d64003be74 100644 --- a/libgo/misc/cgo/test/issue18720.go +++ b/libgo/misc/cgo/test/issue18720.go @@ -12,13 +12,39 @@ package cgotest struct foo { char c; }; #define SIZE_OF(x) sizeof(x) #define SIZE_OF_FOO SIZE_OF(struct foo) +#define VAR1 VAR +#define VAR var +int var = 5; + +#define ADDR &var + +#define CALL fn() +int fn(void) { + return ++var; +} */ import "C" import "testing" func test18720(t *testing.T) { - if C.HELLO_WORLD != "hello\000world" { - t.Fatalf(`expected "hello\000world", but got %q`, C.HELLO_WORLD) + if got, want := C.HELLO_WORLD, "hello\000world"; got != want { + t.Errorf("C.HELLO_WORLD == %q, expected %q", got, want) + } + + if got, want := C.VAR1, C.int(5); got != want { + t.Errorf("C.VAR1 == %v, expected %v", got, want) + } + + if got, want := *C.ADDR, C.int(5); got != want { + t.Errorf("*C.ADDR == %v, expected %v", got, want) + } + + if got, want := C.CALL, C.int(6); got != want { + t.Errorf("C.CALL == %v, expected %v", got, want) + } + + if got, want := C.CALL, C.int(7); got != want { + t.Errorf("C.CALL == %v, expected %v", got, want) } // Issue 20125. diff --git a/libgo/misc/cgo/test/issue19832.go b/libgo/misc/cgo/test/issue19832.go new file mode 100644 index 00000000000..44587770af4 --- /dev/null +++ b/libgo/misc/cgo/test/issue19832.go @@ -0,0 +1,16 @@ +// Copyright 2015 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. + +// Issue 19832. Functions taking a pointer typedef were being expanded and triggering a compiler error. + +package cgotest + +// typedef struct { int i; } *PS; +// void T19832(PS p) {} +import "C" +import "testing" + +func test19832(t *testing.T) { + C.T19832(nil) +} diff --git a/libgo/misc/cgo/test/issue20910.c b/libgo/misc/cgo/test/issue20910.c new file mode 100644 index 00000000000..e8d623fc983 --- /dev/null +++ b/libgo/misc/cgo/test/issue20910.c @@ -0,0 +1,19 @@ +// Copyright 2017 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 <assert.h> +#include <stdlib.h> +#include <string.h> +#include "_cgo_export.h" + +/* Test calling a Go function with multiple return values. */ + +void +callMulti(void) +{ + struct multi_return result = multi(); + assert(strcmp(result.r0, "multi") == 0); + assert(result.r1 == 0); + free(result.r0); +} diff --git a/libgo/misc/cgo/test/issue20910.go b/libgo/misc/cgo/test/issue20910.go new file mode 100644 index 00000000000..69d7d9249ac --- /dev/null +++ b/libgo/misc/cgo/test/issue20910.go @@ -0,0 +1,19 @@ +// Copyright 2017 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 cgotest + +//void callMulti(void); +import "C" + +import "testing" + +//export multi +func multi() (*C.char, C.int) { + return C.CString("multi"), 0 +} + +func test20910(t *testing.T) { + C.callMulti() +} diff --git a/libgo/misc/cgo/test/issue21668.go b/libgo/misc/cgo/test/issue21668.go new file mode 100644 index 00000000000..f15b9202acc --- /dev/null +++ b/libgo/misc/cgo/test/issue21668.go @@ -0,0 +1,13 @@ +// Copyright 2017 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. + +// Fail to guess the kind of the constant "x". +// No runtime test; just make sure it compiles. + +package cgotest + +// const int x = 42; +import "C" + +var issue21668_X = C.x diff --git a/libgo/misc/cgo/test/issue21708.go b/libgo/misc/cgo/test/issue21708.go new file mode 100644 index 00000000000..d413e3c57a9 --- /dev/null +++ b/libgo/misc/cgo/test/issue21708.go @@ -0,0 +1,16 @@ +// Copyright 2017 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 cgotest + +// #include <stdint.h> +// #define CAST_TO_INT64 (int64_t)(-1) +import "C" +import "testing" + +func test21708(t *testing.T) { + if got, want := C.CAST_TO_INT64, -1; got != want { + t.Errorf("C.CAST_TO_INT64 == %v, expected %v", got, want) + } +} diff --git a/libgo/misc/cgo/test/issue21809.go b/libgo/misc/cgo/test/issue21809.go new file mode 100644 index 00000000000..a3a6b88897e --- /dev/null +++ b/libgo/misc/cgo/test/issue21809.go @@ -0,0 +1,45 @@ +// Copyright 2017 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 cgotest + +// Issue 21809. Compile C `typedef` to go type aliases. + +// typedef long MySigned_t; +// /* tests alias-to-alias */ +// typedef MySigned_t MySigned2_t; +// +// long takes_long(long x) { return x * x; } +// MySigned_t takes_typedef(MySigned_t x) { return x * x; } +import "C" + +import "testing" + +func test21809(t *testing.T) { + longVar := C.long(3) + typedefVar := C.MySigned_t(4) + typedefTypedefVar := C.MySigned2_t(5) + + // all three should be considered identical to `long` + if ret := C.takes_long(longVar); ret != 9 { + t.Errorf("got %v but expected %v", ret, 9) + } + if ret := C.takes_long(typedefVar); ret != 16 { + t.Errorf("got %v but expected %v", ret, 16) + } + if ret := C.takes_long(typedefTypedefVar); ret != 25 { + t.Errorf("got %v but expected %v", ret, 25) + } + + // They should also be identical to the typedef'd type + if ret := C.takes_typedef(longVar); ret != 9 { + t.Errorf("got %v but expected %v", ret, 9) + } + if ret := C.takes_typedef(typedefVar); ret != 16 { + t.Errorf("got %v but expected %v", ret, 16) + } + if ret := C.takes_typedef(typedefTypedefVar); ret != 25 { + t.Errorf("got %v but expected %v", ret, 25) + } +} diff --git a/libgo/misc/cgo/test/issue21897.go b/libgo/misc/cgo/test/issue21897.go new file mode 100644 index 00000000000..d13246bd84a --- /dev/null +++ b/libgo/misc/cgo/test/issue21897.go @@ -0,0 +1,56 @@ +// Copyright 2017 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 darwin,cgo,!internal + +package cgotest + +/* +#cgo LDFLAGS: -framework CoreFoundation +#include <CoreFoundation/CoreFoundation.h> +*/ +import "C" +import ( + "runtime/debug" + "testing" + "unsafe" +) + +func test21897(t *testing.T) { + // Please write barrier, kick in soon. + defer debug.SetGCPercent(debug.SetGCPercent(1)) + + for i := 0; i < 10000; i++ { + testCFNumberRef() + testCFDateRef() + testCFBooleanRef() + // Allocate some memory, so eventually the write barrier is enabled + // and it will see writes of bad pointers in the test* functions below. + byteSliceSink = make([]byte, 1024) + } +} + +var byteSliceSink []byte + +func testCFNumberRef() { + var v int64 = 0 + xCFNumberRef = C.CFNumberCreate(C.kCFAllocatorSystemDefault, C.kCFNumberSInt64Type, unsafe.Pointer(&v)) + //fmt.Printf("CFNumberRef: %x\n", uintptr(unsafe.Pointer(xCFNumberRef))) +} + +var xCFNumberRef C.CFNumberRef + +func testCFDateRef() { + xCFDateRef = C.CFDateCreate(C.kCFAllocatorSystemDefault, 0) // 0 value is 1 Jan 2001 00:00:00 GMT + //fmt.Printf("CFDateRef: %x\n", uintptr(unsafe.Pointer(xCFDateRef))) +} + +var xCFDateRef C.CFDateRef + +func testCFBooleanRef() { + xCFBooleanRef = C.kCFBooleanFalse + //fmt.Printf("CFBooleanRef: %x\n", uintptr(unsafe.Pointer(xCFBooleanRef))) +} + +var xCFBooleanRef C.CFBooleanRef diff --git a/libgo/misc/cgo/test/issue21897b.go b/libgo/misc/cgo/test/issue21897b.go new file mode 100644 index 00000000000..08b5f4d808e --- /dev/null +++ b/libgo/misc/cgo/test/issue21897b.go @@ -0,0 +1,13 @@ +// Copyright 2017 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 !darwin !cgo internal + +package cgotest + +import "testing" + +func test21897(t *testing.T) { + t.Skip("test runs only on darwin+cgo") +} diff --git a/libgo/misc/cgo/test/issue22958.go b/libgo/misc/cgo/test/issue22958.go new file mode 100644 index 00000000000..a5f058fdae1 --- /dev/null +++ b/libgo/misc/cgo/test/issue22958.go @@ -0,0 +1,24 @@ +// Copyright 2017 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 cgotest + +// Test handling of bitfields. + +/* +typedef struct { + unsigned long long f8 : 8; + unsigned long long f16 : 16; + unsigned long long f24 : 24; + unsigned long long f32 : 32; + unsigned long long f40 : 40; + unsigned long long f48 : 48; + unsigned long long f56 : 56; + unsigned long long f64 : 64; +} issue22958Type; +*/ +import "C" + +// Nothing to run, just make sure this compiles. +var Vissue22958 C.issue22958Type diff --git a/libgo/misc/cgo/test/issue6907.go b/libgo/misc/cgo/test/issue6907.go new file mode 100644 index 00000000000..00495ab8e2e --- /dev/null +++ b/libgo/misc/cgo/test/issue6907.go @@ -0,0 +1,33 @@ +// Copyright 2017 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 cgotest + +/* +#include <stdlib.h> +#include <string.h> + +char* Issue6907CopyString(_GoString_ s) { + size_t n; + const char *p; + char *r; + + n = _GoStringLen(s); + p = _GoStringPtr(s); + r = malloc(n + 1); + memmove(r, p, n); + r[n] = '\0'; + return r; +} +*/ +import "C" + +import "testing" + +func test6907(t *testing.T) { + want := "yarn" + if got := C.GoString(C.Issue6907CopyString(want)); got != want { + t.Errorf("C.GoString(C.Issue6907CopyString(%q)) == %q, want %q", want, got, want) + } +} diff --git a/libgo/misc/cgo/test/issue6907export.go b/libgo/misc/cgo/test/issue6907export.go new file mode 100644 index 00000000000..d41899e1a62 --- /dev/null +++ b/libgo/misc/cgo/test/issue6907export.go @@ -0,0 +1,30 @@ +// Copyright 2017 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 cgotest + +/* +extern int CheckIssue6907C(_GoString_); +*/ +import "C" + +import ( + "testing" +) + +const CString = "C string" + +//export CheckIssue6907Go +func CheckIssue6907Go(s string) C.int { + if s == CString { + return 1 + } + return 0 +} + +func test6907Go(t *testing.T) { + if got := C.CheckIssue6907C(CString); got != 1 { + t.Errorf("C.CheckIssue6907C() == %d, want %d", got, 1) + } +} diff --git a/libgo/misc/cgo/test/issue6907export_c.c b/libgo/misc/cgo/test/issue6907export_c.c new file mode 100644 index 00000000000..9b1a4fc630b --- /dev/null +++ b/libgo/misc/cgo/test/issue6907export_c.c @@ -0,0 +1,11 @@ +// Copyright 2017 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 <string.h> + +#include "_cgo_export.h" + +int CheckIssue6907C(_GoString_ s) { + return CheckIssue6907Go(s); +} diff --git a/libgo/misc/cgo/test/issue7978.go b/libgo/misc/cgo/test/issue7978.go index 7fb62e807ba..b057e3eacb2 100644 --- a/libgo/misc/cgo/test/issue7978.go +++ b/libgo/misc/cgo/test/issue7978.go @@ -44,8 +44,8 @@ static void issue7978c(uint32_t *sync) { import "C" import ( - "os" "runtime" + "runtime/debug" "strings" "sync/atomic" "testing" @@ -114,12 +114,7 @@ func test7978(t *testing.T) { if C.HAS_SYNC_FETCH_AND_ADD == 0 { t.Skip("clang required for __sync_fetch_and_add support on darwin/arm") } - if runtime.GOOS == "android" || runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { - t.Skip("GOTRACEBACK is not passed on to the exec wrapper") - } - if os.Getenv("GOTRACEBACK") != "2" { - t.Fatalf("GOTRACEBACK must be 2") - } + debug.SetTraceback("2") issue7978sync = 0 go issue7978go() // test in c code, before callback diff --git a/libgo/misc/cgo/testcarchive/carchive_test.go b/libgo/misc/cgo/testcarchive/carchive_test.go index 4865b806a3e..e3f67955802 100644 --- a/libgo/misc/cgo/testcarchive/carchive_test.go +++ b/libgo/misc/cgo/testcarchive/carchive_test.go @@ -6,6 +6,7 @@ package carchive_test import ( "bufio" + "bytes" "debug/elf" "fmt" "io/ioutil" @@ -139,8 +140,10 @@ func cmdToRun(name string) []string { } func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) { + t.Helper() cmd := exec.Command(buildcmd[0], buildcmd[1:]...) cmd.Env = gopathEnv + t.Log(buildcmd) if out, err := cmd.CombinedOutput(); err != nil { t.Logf("%s", out) t.Fatal(err) @@ -188,7 +191,7 @@ func TestInstall(t *testing.T) { testInstall(t, "./testp1"+exeSuffix, filepath.Join("pkg", libgodir, libgoa), filepath.Join("pkg", libgodir, "libgo.h"), - "go", "install", "-buildmode=c-archive", "libgo") + "go", "install", "-i", "-buildmode=c-archive", "libgo") // Test building libgo other than installing it. // Header files are now present. @@ -523,7 +526,7 @@ func TestPIE(t *testing.T) { os.RemoveAll("pkg") }() - cmd := exec.Command("go", "install", "-buildmode=c-archive", "libgo") + cmd := exec.Command("go", "install", "-i", "-buildmode=c-archive", "libgo") cmd.Env = gopathEnv if out, err := cmd.CombinedOutput(); err != nil { t.Logf("%s", out) @@ -596,6 +599,8 @@ func TestSIGPROF(t *testing.T) { switch GOOS { case "windows", "plan9": t.Skipf("skipping SIGPROF test on %s", GOOS) + case "darwin": + t.Skipf("skipping SIGPROF test on %s; see https://golang.org/issue/19320", GOOS) } t.Parallel() @@ -655,12 +660,29 @@ func TestCompileWithoutShared(t *testing.T) { } exe := "./testnoshared" + exeSuffix - ccArgs := append(cc, "-o", exe, "main5.c", "libgo2.a") + + // In some cases, -no-pie is needed here, but not accepted everywhere. First try + // if -no-pie is accepted. See #22126. + ccArgs := append(cc, "-o", exe, "-no-pie", "main5.c", "libgo2.a") if runtime.Compiler == "gccgo" { ccArgs = append(ccArgs, "-lgo") } t.Log(ccArgs) out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + + // If -no-pie unrecognized, try -nopie if this is possibly clang + if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") { + ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a") + t.Log(ccArgs) + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + } + + // Don't use either -no-pie or -nopie + if err != nil && bytes.Contains(out, []byte("unrecognized")) { + ccArgs := append(cc, "-o", exe, "main5.c", "libgo2.a") + t.Log(ccArgs) + out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput() + } t.Logf("%s", out) if err != nil { t.Fatal(err) diff --git a/libgo/misc/cgo/testcshared/cshared_test.go b/libgo/misc/cgo/testcshared/cshared_test.go new file mode 100644 index 00000000000..49be0923966 --- /dev/null +++ b/libgo/misc/cgo/testcshared/cshared_test.go @@ -0,0 +1,479 @@ +// Copyright 2017 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 cshared_test + +import ( + "debug/elf" + "fmt" + "log" + "os" + "os/exec" + "path" + "path/filepath" + "strings" + "sync" + "testing" + "unicode" +) + +// C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)). +var cc []string + +// An environment with GOPATH=$(pwd). +var gopathEnv []string + +// ".exe" on Windows. +var exeSuffix string + +var GOOS, GOARCH, GOROOT string +var installdir, androiddir string +var libSuffix, libgoname string + +func TestMain(m *testing.M) { + GOOS = goEnv("GOOS") + GOARCH = goEnv("GOARCH") + GOROOT = goEnv("GOROOT") + + if _, err := os.Stat(GOROOT); os.IsNotExist(err) { + log.Fatalf("Unable able to find GOROOT at '%s'", GOROOT) + } + + // Directory where cgo headers and outputs will be installed. + // The installation directory format varies depending on the platform. + installdir = path.Join("pkg", fmt.Sprintf("%s_%s_testcshared", GOOS, GOARCH)) + switch GOOS { + case "darwin": + libSuffix = "dylib" + case "windows": + libSuffix = "dll" + default: + libSuffix = "so" + installdir = path.Join("pkg", fmt.Sprintf("%s_%s_testcshared_shared", GOOS, GOARCH)) + } + + androiddir = fmt.Sprintf("/data/local/tmp/testcshared-%d", os.Getpid()) + if GOOS == "android" { + cmd := exec.Command("adb", "shell", "mkdir", "-p", androiddir) + out, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("setupAndroid failed: %v\n%s\n", err, out) + } + } + + libgoname = "libgo." + libSuffix + + cc = []string{goEnv("CC")} + + out := goEnv("GOGCCFLAGS") + quote := '\000' + start := 0 + lastSpace := true + backslash := false + s := string(out) + for i, c := range s { + if quote == '\000' && unicode.IsSpace(c) { + if !lastSpace { + cc = append(cc, s[start:i]) + lastSpace = true + } + } else { + if lastSpace { + start = i + lastSpace = false + } + if quote == '\000' && !backslash && (c == '"' || c == '\'') { + quote = c + backslash = false + } else if !backslash && quote == c { + quote = '\000' + } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' { + backslash = true + } else { + backslash = false + } + } + } + if !lastSpace { + cc = append(cc, s[start:]) + } + + switch GOOS { + case "darwin": + // For Darwin/ARM. + // TODO(crawshaw): can we do better? + cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...) + case "android": + cc = append(cc, "-pie", "-fuse-ld=gold") + } + libgodir := GOOS + "_" + GOARCH + switch GOOS { + case "darwin": + if GOARCH == "arm" || GOARCH == "arm64" { + libgodir += "_shared" + } + case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": + libgodir += "_shared" + } + cc = append(cc, "-I", filepath.Join("pkg", libgodir)) + + // Build an environment with GOPATH=$(pwd) + dir, err := os.Getwd() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(2) + } + gopathEnv = append(os.Environ(), "GOPATH="+dir) + + if GOOS == "windows" { + exeSuffix = ".exe" + } + + st := m.Run() + + os.Remove(libgoname) + os.RemoveAll("pkg") + cleanupHeaders() + cleanupAndroid() + + os.Exit(st) +} + +func goEnv(key string) string { + out, err := exec.Command("go", "env", key).Output() + if err != nil { + fmt.Fprintf(os.Stderr, "go env %s failed:\n%s", key, err) + fmt.Fprintf(os.Stderr, "%s", err.(*exec.ExitError).Stderr) + os.Exit(2) + } + return strings.TrimSpace(string(out)) +} + +func cmdToRun(name string) string { + return "./" + name + exeSuffix +} + +func adbPush(t *testing.T, filename string) { + if GOOS != "android" { + return + } + args := []string{"adb", "push", filename, fmt.Sprintf("%s/%s", androiddir, filename)} + cmd := exec.Command(args[0], args[1:]...) + if out, err := cmd.CombinedOutput(); err != nil { + t.Fatalf("adb command failed: %v\n%s\n", err, out) + } +} + +func adbRun(t *testing.T, env []string, adbargs ...string) string { + if GOOS != "android" { + t.Fatalf("trying to run adb command when operating system is not android.") + } + args := []string{"adb", "shell"} + // Propagate LD_LIBRARY_PATH to the adb shell invocation. + for _, e := range env { + if strings.Index(e, "LD_LIBRARY_PATH=") != -1 { + adbargs = append([]string{e}, adbargs...) + break + } + } + shellcmd := fmt.Sprintf("cd %s; %s", androiddir, strings.Join(adbargs, " ")) + args = append(args, shellcmd) + cmd := exec.Command(args[0], args[1:]...) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("adb command failed: %v\n%s\n", err, out) + } + return strings.Replace(string(out), "\r", "", -1) +} + +func run(t *testing.T, env []string, args ...string) string { + t.Helper() + cmd := exec.Command(args[0], args[1:]...) + cmd.Env = env + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("command failed: %v\n%v\n%s\n", args, err, out) + } else { + t.Logf("run: %v", args) + } + return string(out) +} + +func runExe(t *testing.T, env []string, args ...string) string { + t.Helper() + if GOOS == "android" { + return adbRun(t, env, args...) + } + return run(t, env, args...) +} + +func runCC(t *testing.T, args ...string) string { + t.Helper() + // This function is run in parallel, so append to a copy of cc + // rather than cc itself. + return run(t, nil, append(append([]string(nil), cc...), args...)...) +} + +func createHeaders() error { + args := []string{"go", "install", "-i", "-buildmode=c-shared", + "-installsuffix", "testcshared", "libgo"} + cmd := exec.Command(args[0], args[1:]...) + cmd.Env = gopathEnv + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out) + } + + args = []string{"go", "build", "-buildmode=c-shared", + "-installsuffix", "testcshared", + "-o", libgoname, + filepath.Join("src", "libgo", "libgo.go")} + cmd = exec.Command(args[0], args[1:]...) + cmd.Env = gopathEnv + out, err = cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out) + } + + if GOOS == "android" { + args = []string{"adb", "push", libgoname, fmt.Sprintf("%s/%s", androiddir, libgoname)} + cmd = exec.Command(args[0], args[1:]...) + out, err = cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("adb command failed: %v\n%s\n", err, out) + } + } + + return nil +} + +var ( + headersOnce sync.Once + headersErr error +) + +func createHeadersOnce(t *testing.T) { + headersOnce.Do(func() { + headersErr = createHeaders() + }) + if headersErr != nil { + t.Fatal(headersErr) + } +} + +func cleanupHeaders() { + os.Remove("libgo.h") +} + +func cleanupAndroid() { + if GOOS != "android" { + return + } + cmd := exec.Command("adb", "shell", "rm", "-rf", androiddir) + out, err := cmd.CombinedOutput() + if err != nil { + log.Fatalf("cleanupAndroid failed: %v\n%s\n", err, out) + } +} + +// test0: exported symbols in shared lib are accessible. +func TestExportedSymbols(t *testing.T) { + t.Parallel() + + cmd := "testp0" + bin := cmdToRun(cmd) + + createHeadersOnce(t) + + runCC(t, "-I", installdir, "-o", cmd, "main0.c", libgoname) + adbPush(t, cmd) + + defer os.Remove(bin) + + out := runExe(t, append(gopathEnv, "LD_LIBRARY_PATH=."), bin) + if strings.TrimSpace(out) != "PASS" { + t.Error(out) + } +} + +// test1: shared library can be dynamically loaded and exported symbols are accessible. +func TestExportedSymbolsWithDynamicLoad(t *testing.T) { + t.Parallel() + + if GOOS == "windows" { + t.Logf("Skipping on %s", GOOS) + return + } + + cmd := "testp1" + bin := cmdToRun(cmd) + + createHeadersOnce(t) + + runCC(t, "-o", cmd, "main1.c", "-ldl") + adbPush(t, cmd) + + defer os.Remove(bin) + + out := runExe(t, nil, bin, "./"+libgoname) + if strings.TrimSpace(out) != "PASS" { + t.Error(out) + } +} + +// test2: tests libgo2 which does not export any functions. +func TestUnexportedSymbols(t *testing.T) { + t.Parallel() + + if GOOS == "windows" { + t.Logf("Skipping on %s", GOOS) + return + } + + cmd := "testp2" + bin := cmdToRun(cmd) + libname := "libgo2." + libSuffix + + run(t, + gopathEnv, + "go", "build", + "-buildmode=c-shared", + "-installsuffix", "testcshared", + "-o", libname, "libgo2", + ) + adbPush(t, libname) + + linkFlags := "-Wl,--no-as-needed" + if GOOS == "darwin" { + linkFlags = "" + } + + runCC(t, "-o", cmd, "main2.c", linkFlags, libname) + adbPush(t, cmd) + + defer os.Remove(libname) + defer os.Remove(bin) + + out := runExe(t, append(gopathEnv, "LD_LIBRARY_PATH=."), bin) + + if strings.TrimSpace(out) != "PASS" { + t.Error(out) + } +} + +// test3: tests main.main is exported on android. +func TestMainExportedOnAndroid(t *testing.T) { + t.Parallel() + + switch GOOS { + case "android": + break + default: + t.Logf("Skipping on %s", GOOS) + return + } + + cmd := "testp3" + bin := cmdToRun(cmd) + + createHeadersOnce(t) + + runCC(t, "-o", cmd, "main3.c", "-ldl") + adbPush(t, cmd) + + defer os.Remove(bin) + + out := runExe(t, nil, bin, "./"+libgoname) + if strings.TrimSpace(out) != "PASS" { + t.Error(out) + } +} + +func testSignalHandlers(t *testing.T, pkgname, cfile, cmd string) { + libname := pkgname + "." + libSuffix + run(t, + gopathEnv, + "go", "build", + "-buildmode=c-shared", + "-installsuffix", "testcshared", + "-o", libname, pkgname, + ) + adbPush(t, libname) + runCC(t, "-pthread", "-o", cmd, cfile, "-ldl") + adbPush(t, cmd) + + bin := cmdToRun(cmd) + + defer os.Remove(libname) + defer os.Remove(bin) + defer os.Remove(pkgname + ".h") + + out := runExe(t, nil, bin, "./"+libname) + if strings.TrimSpace(out) != "PASS" { + t.Error(run(t, nil, bin, libname, "verbose")) + } +} + +// test4: test signal handlers +func TestSignalHandlers(t *testing.T) { + t.Parallel() + if GOOS == "windows" { + t.Logf("Skipping on %s", GOOS) + return + } + testSignalHandlers(t, "libgo4", "main4.c", "testp4") +} + +// test5: test signal handlers with os/signal.Notify +func TestSignalHandlersWithNotify(t *testing.T) { + t.Parallel() + if GOOS == "windows" { + t.Logf("Skipping on %s", GOOS) + return + } + testSignalHandlers(t, "libgo5", "main5.c", "testp5") +} + +func TestPIE(t *testing.T) { + t.Parallel() + + switch GOOS { + case "linux", "android": + break + default: + t.Logf("Skipping on %s", GOOS) + return + } + + createHeadersOnce(t) + + f, err := elf.Open(libgoname) + if err != nil { + t.Fatalf("elf.Open failed: %v", err) + } + defer f.Close() + + ds := f.SectionByType(elf.SHT_DYNAMIC) + if ds == nil { + t.Fatalf("no SHT_DYNAMIC section") + } + d, err := ds.Data() + if err != nil { + t.Fatalf("can't read SHT_DYNAMIC contents: %v", err) + } + for len(d) > 0 { + var tag elf.DynTag + switch f.Class { + case elf.ELFCLASS32: + tag = elf.DynTag(f.ByteOrder.Uint32(d[:4])) + d = d[8:] + case elf.ELFCLASS64: + tag = elf.DynTag(f.ByteOrder.Uint64(d[:8])) + d = d[16:] + } + if tag == elf.DT_TEXTREL { + t.Fatalf("%s has DT_TEXTREL flag", libgoname) + } + } +} diff --git a/libgo/misc/cgo/testcshared/test.bash b/libgo/misc/cgo/testcshared/test.bash deleted file mode 100644 index 315a0d40367..00000000000 --- a/libgo/misc/cgo/testcshared/test.bash +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2015 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. - -# For testing Android, this script requires adb to push and run compiled -# binaries on a target device. - -set -e - -if [ ! -f src/libgo/libgo.go ]; then - cwd=$(pwd) - echo "misc/cgo/testcshared/test.bash is running in $cwd" 1>&2 - exit 1 -fi - -goos=$(go env GOOS) -goarch=$(go env GOARCH) -goroot=$(go env GOROOT) -if [ ! -d "$goroot" ]; then - echo 'misc/cgo/testcshared/test.bash cannot find GOROOT' 1>&2 - echo '$GOROOT:' "$GOROOT" 1>&2 - echo 'go env GOROOT:' "$goroot" 1>&2 - exit 1 -fi - -# Directory where cgo headers and outputs will be installed. -# The installation directory format varies depending on the platform. -installdir=pkg/${goos}_${goarch}_testcshared_shared -if [ "${goos}" = "darwin" ]; then - installdir=pkg/${goos}_${goarch}_testcshared -fi - -# Temporary directory on the android device. -androidpath=/data/local/tmp/testcshared-$$ - -function cleanup() { - rm -f libgo.$libext libgo2.$libext libgo4.$libext libgo5.$libext - rm -f libgo.h libgo4.h libgo5.h - rm -f testp testp2 testp3 testp4 testp5 - rm -rf pkg "${goroot}/${installdir}" - - if [ "$goos" = "android" ]; then - adb shell rm -rf "$androidpath" - fi -} -trap cleanup EXIT - -if [ "$goos" = "android" ]; then - adb shell mkdir -p "$androidpath" -fi - -function run() { - case "$goos" in - "android") - local args=$@ - output=$(adb shell "cd ${androidpath}; $@") - output=$(echo $output|tr -d '\r') - case $output in - *PASS) echo "PASS";; - *) echo "$output";; - esac - ;; - *) - echo $(env $@) - ;; - esac -} - -function binpush() { - bin=${1} - if [ "$goos" = "android" ]; then - adb push "$bin" "${androidpath}/${bin}" 2>/dev/null - fi -} - -rm -rf pkg - -suffix="-installsuffix testcshared" - -libext="so" -if [ "$goos" = "darwin" ]; then - libext="dylib" -fi - -# Create the header files. -GOPATH=$(pwd) go install -buildmode=c-shared $suffix libgo - -GOPATH=$(pwd) go build -buildmode=c-shared $suffix -o libgo.$libext src/libgo/libgo.go -binpush libgo.$libext - -if [ "$goos" = "linux" ] || [ "$goos" = "android" ] ; then - if readelf -d libgo.$libext | grep TEXTREL >/dev/null; then - echo "libgo.$libext has TEXTREL set" - exit 1 - fi -fi - -GOGCCFLAGS=$(go env GOGCCFLAGS) -if [ "$goos" = "android" ]; then - GOGCCFLAGS="${GOGCCFLAGS} -pie -fuse-ld=gold" -fi - -status=0 - -# test0: exported symbols in shared lib are accessible. -# TODO(iant): using _shared here shouldn't really be necessary. -$(go env CC) ${GOGCCFLAGS} -I ${installdir} -o testp main0.c ./libgo.$libext -binpush testp - -output=$(run LD_LIBRARY_PATH=. ./testp) -if [ "$output" != "PASS" ]; then - echo "FAIL test0 got ${output}" - status=1 -fi - -# test1: shared library can be dynamically loaded and exported symbols are accessible. -$(go env CC) ${GOGCCFLAGS} -o testp main1.c -ldl -binpush testp -output=$(run ./testp ./libgo.$libext) -if [ "$output" != "PASS" ]; then - echo "FAIL test1 got ${output}" - status=1 -fi - -# test2: tests libgo2 which does not export any functions. -GOPATH=$(pwd) go build -buildmode=c-shared $suffix -o libgo2.$libext libgo2 -binpush libgo2.$libext -linkflags="-Wl,--no-as-needed" -if [ "$goos" = "darwin" ]; then - linkflags="" -fi -$(go env CC) ${GOGCCFLAGS} -o testp2 main2.c $linkflags libgo2.$libext -binpush testp2 -output=$(run LD_LIBRARY_PATH=. ./testp2) -if [ "$output" != "PASS" ]; then - echo "FAIL test2 got ${output}" - status=1 -fi - -# test3: tests main.main is exported on android. -if [ "$goos" = "android" ]; then - $(go env CC) ${GOGCCFLAGS} -o testp3 main3.c -ldl - binpush testp3 - output=$(run ./testp ./libgo.so) - if [ "$output" != "PASS" ]; then - echo "FAIL test3 got ${output}" - status=1 - fi -fi - -# test4: tests signal handlers -GOPATH=$(pwd) go build -buildmode=c-shared $suffix -o libgo4.$libext libgo4 -binpush libgo4.$libext -$(go env CC) ${GOGCCFLAGS} -pthread -o testp4 main4.c -ldl -binpush testp4 -output=$(run ./testp4 ./libgo4.$libext 2>&1) -if test "$output" != "PASS"; then - echo "FAIL test4 got ${output}" - if test "$goos" != "android"; then - echo "re-running test4 in verbose mode" - ./testp4 ./libgo4.$libext verbose - fi - status=1 -fi - -# test5: tests signal handlers with os/signal.Notify -GOPATH=$(pwd) go build -buildmode=c-shared $suffix -o libgo5.$libext libgo5 -binpush libgo5.$libext -$(go env CC) ${GOGCCFLAGS} -pthread -o testp5 main5.c -ldl -binpush testp5 -output=$(run ./testp5 ./libgo5.$libext 2>&1) -if test "$output" != "PASS"; then - echo "FAIL test5 got ${output}" - if test "$goos" != "android"; then - echo "re-running test5 in verbose mode" - ./testp5 ./libgo5.$libext verbose - fi - status=1 -fi - -if test "$libext" = "dylib"; then - # make sure dylibs are well-formed - if ! otool -l libgo*.dylib >/dev/null; then - status=1 - fi -fi - -if test $status = 0; then - echo "ok" -fi - -exit $status diff --git a/libgo/misc/cgo/testplugin/src/host/host.go b/libgo/misc/cgo/testplugin/src/host/host.go index 898f44efa15..0ca17da3def 100644 --- a/libgo/misc/cgo/testplugin/src/host/host.go +++ b/libgo/misc/cgo/testplugin/src/host/host.go @@ -126,14 +126,24 @@ func main() { log.Fatalf(`plugin1.F()=%d, want 17`, gotf) } - // plugin2 has no exported symbols, only an init function. - if _, err := plugin.Open("plugin2.so"); err != nil { + p2, err := plugin.Open("plugin2.so") + if err != nil { log.Fatalf("plugin.Open failed: %v", err) } + // Check that plugin2's init function was called, and + // that it modifies the same global variable as the host. if got, want := common.X, 2; got != want { log.Fatalf("after loading plugin2, common.X=%d, want %d", got, want) } + _, err = plugin.Open("plugin2-dup.so") + if err == nil { + log.Fatal(`plugin.Open("plugin2-dup.so"): duplicate open should have failed`) + } + if s := err.Error(); !strings.Contains(s, "already loaded") { + log.Fatal(`plugin.Open("plugin2.so"): error does not mention "already loaded"`) + } + _, err = plugin.Open("plugin-mismatch.so") if err == nil { log.Fatal(`plugin.Open("plugin-mismatch.so"): should have failed`) @@ -142,6 +152,24 @@ func main() { log.Fatalf(`plugin.Open("plugin-mismatch.so"): error does not mention "different version": %v`, s) } + _, err = plugin.Open("plugin2-dup.so") + if err == nil { + log.Fatal(`plugin.Open("plugin2-dup.so"): duplicate open after bad plugin should have failed`) + } + _, err = plugin.Open("plugin2.so") + if err != nil { + log.Fatalf(`plugin.Open("plugin2.so"): second open with same name failed: %v`, err) + } + + // Test that unexported types with the same names in + // different plugins do not interfere with each other. + // + // See Issue #21386. + UnexportedNameReuse, _ := p.Lookup("UnexportedNameReuse") + UnexportedNameReuse.(func())() + UnexportedNameReuse, _ = p2.Lookup("UnexportedNameReuse") + UnexportedNameReuse.(func())() + testUnnamed() fmt.Println("PASS") diff --git a/libgo/misc/cgo/testplugin/src/issue18584/main.go b/libgo/misc/cgo/testplugin/src/issue18584/main.go new file mode 100644 index 00000000000..c280fd46203 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue18584/main.go @@ -0,0 +1,23 @@ +// Copyright 2017 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 main + +import "plugin" + +func main() { + p, err := plugin.Open("plugin.so") + if err != nil { + panic(err) + } + + sym, err := p.Lookup("G") + if err != nil { + panic(err) + } + g := sym.(func() bool) + if !g() { + panic("expected types to match, Issue #18584") + } +} diff --git a/libgo/misc/cgo/testplugin/src/issue18584/plugin.go b/libgo/misc/cgo/testplugin/src/issue18584/plugin.go new file mode 100644 index 00000000000..be0868d3752 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue18584/plugin.go @@ -0,0 +1,19 @@ +// Copyright 2017 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 main + +import "reflect" + +type C struct { +} + +func F(c *C) *C { + return nil +} + +func G() bool { + var c *C + return reflect.TypeOf(F).Out(0) == reflect.TypeOf(c) +} diff --git a/libgo/misc/cgo/testplugin/src/issue19418/main.go b/libgo/misc/cgo/testplugin/src/issue19418/main.go new file mode 100644 index 00000000000..2ec9f9aaaa2 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue19418/main.go @@ -0,0 +1,29 @@ +// Copyright 2017 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 main + +import ( + "fmt" + "os" + "plugin" +) + +func main() { + p, err := plugin.Open("plugin.so") + if err != nil { + panic(err) + } + + val, err := p.Lookup("Val") + if err != nil { + panic(err) + } + got := *val.(*string) + const want = "linkstr" + if got != want { + fmt.Fprintf(os.Stderr, "issue19418 value is %q, want %q\n", got, want) + os.Exit(2) + } +} diff --git a/libgo/misc/cgo/testplugin/src/issue19418/plugin.go b/libgo/misc/cgo/testplugin/src/issue19418/plugin.go new file mode 100644 index 00000000000..fe93b161431 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue19418/plugin.go @@ -0,0 +1,7 @@ +// Copyright 2017 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 main + +var Val = "val-unset" diff --git a/libgo/misc/cgo/testplugin/src/issue19529/plugin.go b/libgo/misc/cgo/testplugin/src/issue19529/plugin.go new file mode 100644 index 00000000000..ad2df6cc7c7 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue19529/plugin.go @@ -0,0 +1,15 @@ +package main + +import ( + "reflect" +) + +type Foo struct { + Bar string `json:"Bar@baz,omitempty"` +} + +func F() { + println(reflect.TypeOf(Foo{}).Field(0).Tag) +} + +func main() {} diff --git a/libgo/misc/cgo/testplugin/src/issue22175/main.go b/libgo/misc/cgo/testplugin/src/issue22175/main.go new file mode 100644 index 00000000000..9be9bab9dc3 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue22175/main.go @@ -0,0 +1,28 @@ +// Copyright 2017 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 main + +import ( + "fmt" + "os" + "plugin" +) + +func main() { + p2, err := plugin.Open("issue22175_plugin1.so") + if err != nil { + panic(err) + } + f, err := p2.Lookup("F") + if err != nil { + panic(err) + } + got := f.(func() int)() + const want = 971 + if got != want { + fmt.Fprintf(os.Stderr, "issue22175: F()=%d, want %d", got, want) + os.Exit(1) + } +} diff --git a/libgo/misc/cgo/testplugin/src/issue22175/plugin1.go b/libgo/misc/cgo/testplugin/src/issue22175/plugin1.go new file mode 100644 index 00000000000..5ae6cb631e7 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue22175/plugin1.go @@ -0,0 +1,21 @@ +// Copyright 2017 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 main + +import "plugin" + +func F() int { + p2, err := plugin.Open("issue22175_plugin2.so") + if err != nil { + panic(err) + } + g, err := p2.Lookup("G") + if err != nil { + panic(err) + } + return g.(func() int)() +} + +func main() {} diff --git a/libgo/misc/cgo/testplugin/src/issue22175/plugin2.go b/libgo/misc/cgo/testplugin/src/issue22175/plugin2.go new file mode 100644 index 00000000000..f387a192e67 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue22175/plugin2.go @@ -0,0 +1,9 @@ +// Copyright 2017 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 main + +func G() int { return 971 } + +func main() {} diff --git a/libgo/misc/cgo/testplugin/src/issue22295.pkg/main.go b/libgo/misc/cgo/testplugin/src/issue22295.pkg/main.go new file mode 100644 index 00000000000..6cb186e1003 --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue22295.pkg/main.go @@ -0,0 +1,28 @@ +// Copyright 2017 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 ( + "log" + "plugin" +) + +func main() { + p, err := plugin.Open("issue.22295.so") + if err != nil { + log.Fatal(err) + } + f, err := p.Lookup("F") + if err != nil { + log.Fatal(err) + } + const want = 2503 + got := f.(func() int)() + if got != want { + log.Fatalf("got %d, want %d", got, want) + } +} diff --git a/libgo/misc/cgo/testplugin/src/issue22295.pkg/plugin.go b/libgo/misc/cgo/testplugin/src/issue22295.pkg/plugin.go new file mode 100644 index 00000000000..46b08a405bc --- /dev/null +++ b/libgo/misc/cgo/testplugin/src/issue22295.pkg/plugin.go @@ -0,0 +1,16 @@ +// Copyright 2017 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 main + +var f *int + +func init() { + f = new(int) + *f = 2503 +} + +func F() int { return *f } + +func main() {} diff --git a/libgo/misc/cgo/testplugin/src/plugin1/plugin1.go b/libgo/misc/cgo/testplugin/src/plugin1/plugin1.go index edcef2c77e9..0a9fa2f2c1f 100644 --- a/libgo/misc/cgo/testplugin/src/plugin1/plugin1.go +++ b/libgo/misc/cgo/testplugin/src/plugin1/plugin1.go @@ -7,7 +7,10 @@ package main // // No C code required. import "C" -import "common" +import ( + "common" + "reflect" +) func F() int { _ = make([]byte, 1<<21) // trigger stack unwind, Issue #18190. @@ -33,6 +36,21 @@ func init() { call(g) } +type sameNameReusedInPlugins struct { + X string +} + +type sameNameHolder struct { + F *sameNameReusedInPlugins +} + +func UnexportedNameReuse() { + h := sameNameHolder{} + v := reflect.ValueOf(&h).Elem().Field(0) + newval := reflect.New(v.Type().Elem()) + v.Set(newval) +} + func main() { panic("plugin1.main called") } diff --git a/libgo/misc/cgo/testplugin/src/plugin2/plugin2.go b/libgo/misc/cgo/testplugin/src/plugin2/plugin2.go index 9c507fc3658..a67f2de27a7 100644 --- a/libgo/misc/cgo/testplugin/src/plugin2/plugin2.go +++ b/libgo/misc/cgo/testplugin/src/plugin2/plugin2.go @@ -13,6 +13,7 @@ import "C" import ( "common" + "reflect" "strings" ) @@ -22,6 +23,21 @@ func init() { common.X = 2 } +type sameNameReusedInPlugins struct { + X string +} + +type sameNameHolder struct { + F *sameNameReusedInPlugins +} + +func UnexportedNameReuse() { + h := sameNameHolder{} + v := reflect.ValueOf(&h).Elem().Field(0) + newval := reflect.New(v.Type().Elem()) + v.Set(newval) +} + func main() { panic("plugin1.main called") } diff --git a/libgo/misc/cgo/testplugin/test.bash b/libgo/misc/cgo/testplugin/test.bash index 69df5bd2bfa..18e3803bf42 100644 --- a/libgo/misc/cgo/testplugin/test.bash +++ b/libgo/misc/cgo/testplugin/test.bash @@ -15,38 +15,73 @@ goos=$(go env GOOS) goarch=$(go env GOARCH) function cleanup() { - rm -f plugin*.so unnamed*.so iface*.so - rm -rf host pkg sub iface issue18676 issue19534 + rm -f plugin*.so unnamed*.so iface*.so issue* + rm -rf host pkg sub iface } trap cleanup EXIT rm -rf pkg sub mkdir sub -GOPATH=$(pwd) go build -buildmode=plugin plugin1 -GOPATH=$(pwd) go build -buildmode=plugin plugin2 -GOPATH=$(pwd)/altpath go build -buildmode=plugin plugin-mismatch -GOPATH=$(pwd) go build -buildmode=plugin -o=sub/plugin1.so sub/plugin1 -GOPATH=$(pwd) go build -buildmode=plugin unnamed1.go -GOPATH=$(pwd) go build -buildmode=plugin unnamed2.go -GOPATH=$(pwd) go build host +GOPATH=$(pwd) go build -i -gcflags "$GO_GCFLAGS" -buildmode=plugin plugin1 +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin plugin2 +cp plugin2.so plugin2-dup.so +GOPATH=$(pwd)/altpath go build -gcflags "$GO_GCFLAGS" -buildmode=plugin plugin-mismatch +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o=sub/plugin1.so sub/plugin1 +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o=unnamed1.so unnamed1/main.go +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o=unnamed2.so unnamed2/main.go +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" host LD_LIBRARY_PATH=$(pwd) ./host # Test that types and itabs get properly uniqified. -GOPATH=$(pwd) go build -buildmode=plugin iface_a -GOPATH=$(pwd) go build -buildmode=plugin iface_b -GOPATH=$(pwd) go build iface +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin iface_a +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin iface_b +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" iface LD_LIBRARY_PATH=$(pwd) ./iface +function _timeout() ( + set -e + $2 & + p=$! + (sleep $1; kill $p 2>/dev/null) & + p2=$! + wait $p 2>/dev/null + kill -0 $p2 2>/dev/null +) + # Test for issue 18676 - make sure we don't add the same itab twice. # The buggy code hangs forever, so use a timeout to check for that. -GOPATH=$(pwd) go build -buildmode=plugin -o plugin.so src/issue18676/plugin.go -GOPATH=$(pwd) go build -o issue18676 src/issue18676/main.go -timeout 10s ./issue18676 +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o plugin.so src/issue18676/plugin.go +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue18676 src/issue18676/main.go +_timeout 10s ./issue18676 # Test for issue 19534 - that we can load a plugin built in a path with non-alpha # characters -GOPATH=$(pwd) go build -buildmode=plugin -ldflags='-pluginpath=issue.19534' -o plugin.so src/issue19534/plugin.go -GOPATH=$(pwd) go build -o issue19534 src/issue19534/main.go +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -ldflags='-pluginpath=issue.19534' -o plugin.so src/issue19534/plugin.go +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue19534 src/issue19534/main.go ./issue19534 + +# Test for issue 18584 +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o plugin.so src/issue18584/plugin.go +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue18584 src/issue18584/main.go +./issue18584 + +# Test for issue 19418 +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin "-ldflags=-X main.Val=linkstr" -o plugin.so src/issue19418/plugin.go +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue19418 src/issue19418/main.go +./issue19418 + +# Test for issue 19529 +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o plugin.so src/issue19529/plugin.go + +# Test for issue 22175 +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o issue22175_plugin1.so src/issue22175/plugin1.go +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o issue22175_plugin2.so src/issue22175/plugin2.go +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue22175 src/issue22175/main.go +./issue22175 + +# Test for issue 22295 +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -buildmode=plugin -o issue.22295.so issue22295.pkg +GOPATH=$(pwd) go build -gcflags "$GO_GCFLAGS" -o issue22295 src/issue22295.pkg/main.go +./issue22295 diff --git a/libgo/misc/cgo/testplugin/unnamed1.go b/libgo/misc/cgo/testplugin/unnamed1/main.go index 5c1df086d76..5c1df086d76 100644 --- a/libgo/misc/cgo/testplugin/unnamed1.go +++ b/libgo/misc/cgo/testplugin/unnamed1/main.go diff --git a/libgo/misc/cgo/testplugin/unnamed2.go b/libgo/misc/cgo/testplugin/unnamed2/main.go index 7ef66109c5c..7ef66109c5c 100644 --- a/libgo/misc/cgo/testplugin/unnamed2.go +++ b/libgo/misc/cgo/testplugin/unnamed2/main.go diff --git a/libgo/misc/cgo/testsanitizers/cc_test.go b/libgo/misc/cgo/testsanitizers/cc_test.go new file mode 100644 index 00000000000..cacb0d93df7 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/cc_test.go @@ -0,0 +1,441 @@ +// Copyright 2017 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. + +// sanitizers_test checks the use of Go with sanitizers like msan, asan, etc. +// See https://github.com/google/sanitizers. +package sanitizers_test + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "regexp" + "strconv" + "strings" + "sync" + "syscall" + "testing" + "unicode" +) + +var overcommit struct { + sync.Once + value int + err error +} + +// requireOvercommit skips t if the kernel does not allow overcommit. +func requireOvercommit(t *testing.T) { + t.Helper() + + overcommit.Once.Do(func() { + var out []byte + out, overcommit.err = ioutil.ReadFile("/proc/sys/vm/overcommit_memory") + if overcommit.err != nil { + return + } + overcommit.value, overcommit.err = strconv.Atoi(string(bytes.TrimSpace(out))) + }) + + if overcommit.err != nil { + t.Skipf("couldn't determine vm.overcommit_memory (%v); assuming no overcommit", overcommit.err) + } + if overcommit.value == 2 { + t.Skip("vm.overcommit_memory=2") + } +} + +var env struct { + sync.Once + m map[string]string + err error +} + +// goEnv returns the output of $(go env) as a map. +func goEnv(key string) (string, error) { + env.Once.Do(func() { + var out []byte + out, env.err = exec.Command("go", "env", "-json").Output() + if env.err != nil { + return + } + + env.m = make(map[string]string) + env.err = json.Unmarshal(out, &env.m) + }) + if env.err != nil { + return "", env.err + } + + v, ok := env.m[key] + if !ok { + return "", fmt.Errorf("`go env`: no entry for %v", key) + } + return v, nil +} + +// replaceEnv sets the key environment variable to value in cmd. +func replaceEnv(cmd *exec.Cmd, key, value string) { + if cmd.Env == nil { + cmd.Env = os.Environ() + } + cmd.Env = append(cmd.Env, key+"="+value) +} + +// mustRun executes t and fails cmd with a well-formatted message if it fails. +func mustRun(t *testing.T, cmd *exec.Cmd) { + t.Helper() + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%#q exited with %v\n%s", strings.Join(cmd.Args, " "), err, out) + } +} + +// cc returns a cmd that executes `$(go env CC) $(go env GOGCCFLAGS) $args`. +func cc(args ...string) (*exec.Cmd, error) { + CC, err := goEnv("CC") + if err != nil { + return nil, err + } + + GOGCCFLAGS, err := goEnv("GOGCCFLAGS") + if err != nil { + return nil, err + } + + // Split GOGCCFLAGS, respecting quoting. + // + // TODO(bcmills): This code also appears in + // misc/cgo/testcarchive/carchive_test.go, and perhaps ought to go in + // src/cmd/dist/test.go as well. Figure out where to put it so that it can be + // shared. + var flags []string + quote := '\000' + start := 0 + lastSpace := true + backslash := false + for i, c := range GOGCCFLAGS { + if quote == '\000' && unicode.IsSpace(c) { + if !lastSpace { + flags = append(flags, GOGCCFLAGS[start:i]) + lastSpace = true + } + } else { + if lastSpace { + start = i + lastSpace = false + } + if quote == '\000' && !backslash && (c == '"' || c == '\'') { + quote = c + backslash = false + } else if !backslash && quote == c { + quote = '\000' + } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' { + backslash = true + } else { + backslash = false + } + } + } + if !lastSpace { + flags = append(flags, GOGCCFLAGS[start:]) + } + + cmd := exec.Command(CC, flags...) + cmd.Args = append(cmd.Args, args...) + return cmd, nil +} + +type version struct { + name string + major, minor int +} + +var compiler struct { + sync.Once + version + err error +} + +// compilerVersion detects the version of $(go env CC). +// +// It returns a non-nil error if the compiler matches a known version schema but +// the version could not be parsed, or if $(go env CC) could not be determined. +func compilerVersion() (version, error) { + compiler.Once.Do(func() { + compiler.err = func() error { + compiler.name = "unknown" + + cmd, err := cc("--version") + if err != nil { + return err + } + out, err := cmd.Output() + if err != nil { + // Compiler does not support "--version" flag: not Clang or GCC. + return nil + } + + var match [][]byte + if bytes.HasPrefix(out, []byte("gcc")) { + compiler.name = "gcc" + + cmd, err := cc("-dumpversion") + if err != nil { + return err + } + out, err := cmd.Output() + if err != nil { + // gcc, but does not support gcc's "-dumpversion" flag?! + return err + } + gccRE := regexp.MustCompile(`(\d+)\.(\d+)`) + match = gccRE.FindSubmatch(out) + } else { + clangRE := regexp.MustCompile(`clang version (\d+)\.(\d+)`) + if match = clangRE.FindSubmatch(out); len(match) > 0 { + compiler.name = "clang" + } + } + + if len(match) < 3 { + return nil // "unknown" + } + if compiler.major, err = strconv.Atoi(string(match[1])); err != nil { + return err + } + if compiler.minor, err = strconv.Atoi(string(match[2])); err != nil { + return err + } + return nil + }() + }) + return compiler.version, compiler.err +} + +type compilerCheck struct { + once sync.Once + err error + skip bool // If true, skip with err instead of failing with it. +} + +type config struct { + sanitizer string + + cFlags, ldFlags, goFlags []string + + sanitizerCheck, runtimeCheck compilerCheck +} + +var configs struct { + sync.Mutex + m map[string]*config +} + +// configure returns the configuration for the given sanitizer. +func configure(sanitizer string) *config { + configs.Lock() + defer configs.Unlock() + if c, ok := configs.m[sanitizer]; ok { + return c + } + + c := &config{ + sanitizer: sanitizer, + cFlags: []string{"-fsanitize=" + sanitizer}, + ldFlags: []string{"-fsanitize=" + sanitizer}, + } + + if testing.Verbose() { + c.goFlags = append(c.goFlags, "-x") + } + + switch sanitizer { + case "memory": + c.goFlags = append(c.goFlags, "-msan") + + case "thread": + c.goFlags = append(c.goFlags, "--installsuffix=tsan") + compiler, _ := compilerVersion() + if compiler.name == "gcc" { + c.cFlags = append(c.cFlags, "-fPIC") + c.ldFlags = append(c.ldFlags, "-fPIC", "-static-libtsan") + } + + default: + panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer)) + } + + if configs.m == nil { + configs.m = make(map[string]*config) + } + configs.m[sanitizer] = c + return c +} + +// goCmd returns a Cmd that executes "go $subcommand $args" with appropriate +// additional flags and environment. +func (c *config) goCmd(subcommand string, args ...string) *exec.Cmd { + cmd := exec.Command("go", subcommand) + cmd.Args = append(cmd.Args, c.goFlags...) + cmd.Args = append(cmd.Args, args...) + replaceEnv(cmd, "CGO_CFLAGS", strings.Join(c.cFlags, " ")) + replaceEnv(cmd, "CGO_LDFLAGS", strings.Join(c.ldFlags, " ")) + return cmd +} + +// skipIfCSanitizerBroken skips t if the C compiler does not produce working +// binaries as configured. +func (c *config) skipIfCSanitizerBroken(t *testing.T) { + check := &c.sanitizerCheck + check.once.Do(func() { + check.skip, check.err = c.checkCSanitizer() + }) + if check.err != nil { + t.Helper() + if check.skip { + t.Skip(check.err) + } + t.Fatal(check.err) + } +} + +var cMain = []byte(` +int main() { + return 0; +} +`) + +func (c *config) checkCSanitizer() (skip bool, err error) { + dir, err := ioutil.TempDir("", c.sanitizer) + if err != nil { + return false, fmt.Errorf("failed to create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + src := filepath.Join(dir, "return0.c") + if err := ioutil.WriteFile(src, cMain, 0600); err != nil { + return false, fmt.Errorf("failed to write C source file: %v", err) + } + + dst := filepath.Join(dir, "return0") + cmd, err := cc(c.cFlags...) + if err != nil { + return false, err + } + cmd.Args = append(cmd.Args, c.ldFlags...) + cmd.Args = append(cmd.Args, "-o", dst, src) + out, err := cmd.CombinedOutput() + if err != nil { + if bytes.Contains(out, []byte("-fsanitize")) && + (bytes.Contains(out, []byte("unrecognized")) || + bytes.Contains(out, []byte("unsupported"))) { + return true, errors.New(string(out)) + } + return true, fmt.Errorf("%#q failed: %v\n%s", strings.Join(cmd.Args, " "), err, out) + } + + if out, err := exec.Command(dst).CombinedOutput(); err != nil { + if os.IsNotExist(err) { + return true, fmt.Errorf("%#q failed to produce executable: %v", strings.Join(cmd.Args, " "), err) + } + snippet := bytes.SplitN(out, []byte{'\n'}, 2)[0] + return true, fmt.Errorf("%#q generated broken executable: %v\n%s", strings.Join(cmd.Args, " "), err, snippet) + } + + return false, nil +} + +// skipIfRuntimeIncompatible skips t if the Go runtime is suspected not to work +// with cgo as configured. +func (c *config) skipIfRuntimeIncompatible(t *testing.T) { + check := &c.runtimeCheck + check.once.Do(func() { + check.skip, check.err = c.checkRuntime() + }) + if check.err != nil { + t.Helper() + if check.skip { + t.Skip(check.err) + } + t.Fatal(check.err) + } +} + +func (c *config) checkRuntime() (skip bool, err error) { + if c.sanitizer != "thread" { + return false, nil + } + + // libcgo.h sets CGO_TSAN if it detects TSAN support in the C compiler. + // Dump the preprocessor defines to check that that works. + // (Sometimes it doesn't: see https://golang.org/issue/15983.) + cmd, err := cc(c.cFlags...) + if err != nil { + return false, err + } + cmd.Args = append(cmd.Args, "-dM", "-E", "../../../src/runtime/cgo/libcgo.h") + out, err := cmd.CombinedOutput() + if err != nil { + return false, fmt.Errorf("%#q exited with %v\n%s", strings.Join(cmd.Args, " "), err, out) + } + if !bytes.Contains(out, []byte("#define CGO_TSAN")) { + return true, fmt.Errorf("%#q did not define CGO_TSAN") + } + return false, nil +} + +// srcPath returns the path to the given file relative to this test's source tree. +func srcPath(path string) string { + return filepath.Join("src", path) +} + +// A tempDir manages a temporary directory within a test. +type tempDir struct { + base string +} + +func (d *tempDir) RemoveAll(t *testing.T) { + t.Helper() + if d.base == "" { + return + } + if err := os.RemoveAll(d.base); err != nil { + t.Fatal("Failed to remove temp dir: %v", err) + } +} + +func (d *tempDir) Join(name string) string { + return filepath.Join(d.base, name) +} + +func newTempDir(t *testing.T) *tempDir { + t.Helper() + dir, err := ioutil.TempDir("", filepath.Dir(t.Name())) + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + return &tempDir{base: dir} +} + +// hangProneCmd returns an exec.Cmd for a command that is likely to hang. +// +// If one of these tests hangs, the caller is likely to kill the test process +// using SIGINT, which will be sent to all of the processes in the test's group. +// Unfortunately, TSAN in particular is prone to dropping signals, so the SIGINT +// may terminate the test binary but leave the subprocess running. hangProneCmd +// configures subprocess to receive SIGKILL instead to ensure that it won't +// leak. +func hangProneCmd(name string, arg ...string) *exec.Cmd { + cmd := exec.Command(name, arg...) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Pdeathsig: syscall.SIGKILL, + } + return cmd +} diff --git a/libgo/misc/cgo/testsanitizers/cshared_test.go b/libgo/misc/cgo/testsanitizers/cshared_test.go new file mode 100644 index 00000000000..56063ea6201 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/cshared_test.go @@ -0,0 +1,74 @@ +// Copyright 2017 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 sanitizers_test + +import ( + "fmt" + "io/ioutil" + "strings" + "testing" +) + +func TestShared(t *testing.T) { + t.Parallel() + requireOvercommit(t) + + GOOS, err := goEnv("GOOS") + if err != nil { + t.Fatal(err) + } + libExt := "so" + if GOOS == "darwin" { + libExt = "dylib" + } + + cases := []struct { + src string + sanitizer string + }{ + { + src: "msan_shared.go", + sanitizer: "memory", + }, + { + src: "tsan_shared.go", + sanitizer: "thread", + }, + } + + for _, tc := range cases { + tc := tc + name := strings.TrimSuffix(tc.src, ".go") + t.Run(name, func(t *testing.T) { + t.Parallel() + config := configure(tc.sanitizer) + config.skipIfCSanitizerBroken(t) + + dir := newTempDir(t) + defer dir.RemoveAll(t) + + lib := dir.Join(fmt.Sprintf("lib%s.%s", name, libExt)) + mustRun(t, config.goCmd("build", "-buildmode=c-shared", "-o", lib, srcPath(tc.src))) + + cSrc := dir.Join("main.c") + if err := ioutil.WriteFile(cSrc, cMain, 0600); err != nil { + t.Fatalf("failed to write C source file: %v", err) + } + + dstBin := dir.Join(name) + cmd, err := cc(config.cFlags...) + if err != nil { + t.Fatal(err) + } + cmd.Args = append(cmd.Args, config.ldFlags...) + cmd.Args = append(cmd.Args, "-o", dstBin, cSrc, lib) + mustRun(t, cmd) + + cmd = hangProneCmd(dstBin) + replaceEnv(cmd, "LD_LIBRARY_PATH", ".") + mustRun(t, cmd) + }) + } +} diff --git a/libgo/misc/cgo/testsanitizers/msan_test.go b/libgo/misc/cgo/testsanitizers/msan_test.go new file mode 100644 index 00000000000..af5afa9ee48 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/msan_test.go @@ -0,0 +1,55 @@ +// Copyright 2017 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 sanitizers_test + +import ( + "strings" + "testing" +) + +func TestMSAN(t *testing.T) { + t.Parallel() + requireOvercommit(t) + config := configure("memory") + config.skipIfCSanitizerBroken(t) + + mustRun(t, config.goCmd("build", "std")) + + cases := []struct { + src string + wantErr bool + }{ + {src: "msan.go"}, + {src: "msan2.go"}, + {src: "msan2_cmsan.go"}, + {src: "msan3.go"}, + {src: "msan4.go"}, + {src: "msan5.go"}, + {src: "msan_fail.go", wantErr: true}, + } + for _, tc := range cases { + tc := tc + name := strings.TrimSuffix(tc.src, ".go") + t.Run(name, func(t *testing.T) { + t.Parallel() + + dir := newTempDir(t) + defer dir.RemoveAll(t) + + outPath := dir.Join(name) + mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src))) + + cmd := hangProneCmd(outPath) + if tc.wantErr { + out, err := cmd.CombinedOutput() + if err != nil { + return + } + t.Fatalf("%#q exited without error; want MSAN failure\n%s", strings.Join(cmd.Args, " "), out) + } + mustRun(t, cmd) + }) + } +} diff --git a/libgo/misc/cgo/testsanitizers/msan.go b/libgo/misc/cgo/testsanitizers/src/msan.go index 7915fa84f60..7915fa84f60 100644 --- a/libgo/misc/cgo/testsanitizers/msan.go +++ b/libgo/misc/cgo/testsanitizers/src/msan.go diff --git a/libgo/misc/cgo/testsanitizers/msan2.go b/libgo/misc/cgo/testsanitizers/src/msan2.go index 6690cb034fc..6690cb034fc 100644 --- a/libgo/misc/cgo/testsanitizers/msan2.go +++ b/libgo/misc/cgo/testsanitizers/src/msan2.go diff --git a/libgo/misc/cgo/testsanitizers/src/msan2_cmsan.go b/libgo/misc/cgo/testsanitizers/src/msan2_cmsan.go new file mode 100644 index 00000000000..8fdaea90c97 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/src/msan2_cmsan.go @@ -0,0 +1,38 @@ +// Copyright 2015 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 main + +/* +#cgo LDFLAGS: -fsanitize=memory +#cgo CPPFLAGS: -fsanitize=memory + +#include <string.h> +#include <stdint.h> +#include <stdlib.h> + +void f(int32_t *p, int n) { + int32_t * volatile q = (int32_t *)malloc(sizeof(int32_t) * n); + memcpy(p, q, n * sizeof(*p)); + free(q); +} + +void g(int32_t *p, int n) { + if (p[4] != 1) { + abort(); + } +} +*/ +import "C" + +import ( + "unsafe" +) + +func main() { + a := make([]int32, 10) + C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) + a[4] = 1 + C.g((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a))) +} diff --git a/libgo/misc/cgo/testsanitizers/msan3.go b/libgo/misc/cgo/testsanitizers/src/msan3.go index 61a9c29e1a9..61a9c29e1a9 100644 --- a/libgo/misc/cgo/testsanitizers/msan3.go +++ b/libgo/misc/cgo/testsanitizers/src/msan3.go diff --git a/libgo/misc/cgo/testsanitizers/msan4.go b/libgo/misc/cgo/testsanitizers/src/msan4.go index 6c91ff5f091..6c91ff5f091 100644 --- a/libgo/misc/cgo/testsanitizers/msan4.go +++ b/libgo/misc/cgo/testsanitizers/src/msan4.go diff --git a/libgo/misc/cgo/testsanitizers/msan5.go b/libgo/misc/cgo/testsanitizers/src/msan5.go index f1479eb8a00..f1479eb8a00 100644 --- a/libgo/misc/cgo/testsanitizers/msan5.go +++ b/libgo/misc/cgo/testsanitizers/src/msan5.go diff --git a/libgo/misc/cgo/testsanitizers/msan_fail.go b/libgo/misc/cgo/testsanitizers/src/msan_fail.go index 4c8dab34f6e..4c8dab34f6e 100644 --- a/libgo/misc/cgo/testsanitizers/msan_fail.go +++ b/libgo/misc/cgo/testsanitizers/src/msan_fail.go diff --git a/libgo/misc/cgo/testsanitizers/msan_shared.go b/libgo/misc/cgo/testsanitizers/src/msan_shared.go index 966947cac35..966947cac35 100644 --- a/libgo/misc/cgo/testsanitizers/msan_shared.go +++ b/libgo/misc/cgo/testsanitizers/src/msan_shared.go diff --git a/libgo/misc/cgo/testsanitizers/tsan.go b/libgo/misc/cgo/testsanitizers/src/tsan.go index 6c377a701fb..6c377a701fb 100644 --- a/libgo/misc/cgo/testsanitizers/tsan.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan.go diff --git a/libgo/misc/cgo/testsanitizers/tsan10.go b/libgo/misc/cgo/testsanitizers/src/tsan10.go index a40f2455537..a40f2455537 100644 --- a/libgo/misc/cgo/testsanitizers/tsan10.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan10.go diff --git a/libgo/misc/cgo/testsanitizers/tsan11.go b/libgo/misc/cgo/testsanitizers/src/tsan11.go index 70ac9c8ae2c..70ac9c8ae2c 100644 --- a/libgo/misc/cgo/testsanitizers/tsan11.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan11.go diff --git a/libgo/misc/cgo/testsanitizers/tsan12.go b/libgo/misc/cgo/testsanitizers/src/tsan12.go index 3e767eee1f8..3e767eee1f8 100644 --- a/libgo/misc/cgo/testsanitizers/tsan12.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan12.go diff --git a/libgo/misc/cgo/testsanitizers/tsan2.go b/libgo/misc/cgo/testsanitizers/src/tsan2.go index 5018a1987ca..5018a1987ca 100644 --- a/libgo/misc/cgo/testsanitizers/tsan2.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan2.go diff --git a/libgo/misc/cgo/testsanitizers/tsan3.go b/libgo/misc/cgo/testsanitizers/src/tsan3.go index 87f6c80f1b1..87f6c80f1b1 100644 --- a/libgo/misc/cgo/testsanitizers/tsan3.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan3.go diff --git a/libgo/misc/cgo/testsanitizers/tsan4.go b/libgo/misc/cgo/testsanitizers/src/tsan4.go index f0c76d84116..f0c76d84116 100644 --- a/libgo/misc/cgo/testsanitizers/tsan4.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan4.go diff --git a/libgo/misc/cgo/testsanitizers/tsan5.go b/libgo/misc/cgo/testsanitizers/src/tsan5.go index 1214a7743b6..1214a7743b6 100644 --- a/libgo/misc/cgo/testsanitizers/tsan5.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan5.go diff --git a/libgo/misc/cgo/testsanitizers/tsan6.go b/libgo/misc/cgo/testsanitizers/src/tsan6.go index c96f08d2f37..c96f08d2f37 100644 --- a/libgo/misc/cgo/testsanitizers/tsan6.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan6.go diff --git a/libgo/misc/cgo/testsanitizers/tsan7.go b/libgo/misc/cgo/testsanitizers/src/tsan7.go index 2fb9e45ee2d..2fb9e45ee2d 100644 --- a/libgo/misc/cgo/testsanitizers/tsan7.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan7.go diff --git a/libgo/misc/cgo/testsanitizers/tsan8.go b/libgo/misc/cgo/testsanitizers/src/tsan8.go index 88d82a60789..88d82a60789 100644 --- a/libgo/misc/cgo/testsanitizers/tsan8.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan8.go diff --git a/libgo/misc/cgo/testsanitizers/tsan9.go b/libgo/misc/cgo/testsanitizers/src/tsan9.go index f166d8b495a..f166d8b495a 100644 --- a/libgo/misc/cgo/testsanitizers/tsan9.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan9.go diff --git a/libgo/misc/cgo/testsanitizers/tsan_shared.go b/libgo/misc/cgo/testsanitizers/src/tsan_shared.go index 55ff67ecbaf..55ff67ecbaf 100644 --- a/libgo/misc/cgo/testsanitizers/tsan_shared.go +++ b/libgo/misc/cgo/testsanitizers/src/tsan_shared.go diff --git a/libgo/misc/cgo/testsanitizers/test.bash b/libgo/misc/cgo/testsanitizers/test.bash deleted file mode 100644 index 9f80af6c507..00000000000 --- a/libgo/misc/cgo/testsanitizers/test.bash +++ /dev/null @@ -1,233 +0,0 @@ -#!/usr/bin/env bash -# Copyright 2015 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. - -# This directory is intended to test the use of Go with sanitizers -# like msan, asan, etc. See https://github.com/google/sanitizers . - -set -e - -# The sanitizers were originally developed with clang, so prefer it. -CC=cc -if test -x "$(type -p clang)"; then - CC=clang -fi -export CC - -if [ "$(sysctl -n vm.overcommit_memory)" = 2 ]; then - echo "skipping msan/tsan tests: vm.overcommit_memory=2" >&2 - exit 0 -fi - -msan=yes - -TMPDIR=${TMPDIR:-/tmp} -echo 'int main() { return 0; }' > ${TMPDIR}/testsanitizers$$.c -if $CC -fsanitize=memory -o ${TMPDIR}/testsanitizers$$ ${TMPDIR}/testsanitizers$$.c 2>&1 | grep "unrecognized" >& /dev/null; then - echo "skipping msan tests: $CC -fsanitize=memory not supported" - msan=no -elif ! test -x ${TMPDIR}/testsanitizers$$; then - echo "skipping msan tests: $CC -fsanitize-memory did not generate an executable" - msan=no -elif ! ${TMPDIR}/testsanitizers$$ >/dev/null 2>&1; then - echo "skipping msan tests: $CC -fsanitize-memory generates broken executable" - msan=no -fi -rm -f ${TMPDIR}/testsanitizers$$.* - -tsan=yes - -# The memory and thread sanitizers in versions of clang before 3.6 -# don't work with Go. -if test "$msan" = "yes" && $CC --version | grep clang >& /dev/null; then - ver=$($CC --version | sed -e 's/.* version \([0-9.-]*\).*/\1/') - major=$(echo $ver | sed -e 's/\([0-9]*\).*/\1/') - minor=$(echo $ver | sed -e 's/[0-9]*\.\([0-9]*\).*/\1/') - if test "$major" -lt 3 || test "$major" -eq 3 -a "$minor" -lt 6; then - echo "skipping msan/tsan tests: clang version $major.$minor (older than 3.6)" - msan=no - tsan=no - fi - - # Clang before 3.8 does not work with Linux at or after 4.1. - # golang.org/issue/12898. - if test "$msan" = "yes" -a "$major" -lt 3 || test "$major" -eq 3 -a "$minor" -lt 8; then - if test "$(uname)" = Linux; then - linuxver=$(uname -r) - linuxmajor=$(echo $linuxver | sed -e 's/\([0-9]*\).*/\1/') - linuxminor=$(echo $linuxver | sed -e 's/[0-9]*\.\([0-9]*\).*/\1/') - if test "$linuxmajor" -gt 4 || test "$linuxmajor" -eq 4 -a "$linuxminor" -ge 1; then - echo "skipping msan/tsan tests: clang version $major.$minor (older than 3.8) incompatible with linux version $linuxmajor.$linuxminor (4.1 or newer)" - msan=no - tsan=no - fi - fi - fi -fi - -status=0 - -testmsanshared() { - goos=$(go env GOOS) - suffix="-installsuffix testsanitizers" - libext="so" - if [ "$goos" = "darwin" ]; then - libext="dylib" - fi - go build -msan -buildmode=c-shared $suffix -o ${TMPDIR}/libmsanshared.$libext msan_shared.go - - echo 'int main() { return 0; }' > ${TMPDIR}/testmsanshared.c - $CC $(go env GOGCCFLAGS) -fsanitize=memory -o ${TMPDIR}/testmsanshared ${TMPDIR}/testmsanshared.c ${TMPDIR}/libmsanshared.$libext - - if ! LD_LIBRARY_PATH=. ${TMPDIR}/testmsanshared; then - echo "FAIL: msan_shared" - status=1 - fi - rm -f ${TMPDIR}/{testmsanshared,testmsanshared.c,libmsanshared.$libext} -} - -if test "$msan" = "yes"; then - if ! go build -msan std; then - echo "FAIL: build -msan std" - status=1 - fi - - if ! go run -msan msan.go; then - echo "FAIL: msan" - status=1 - fi - - if ! CGO_LDFLAGS="-fsanitize=memory" CGO_CPPFLAGS="-fsanitize=memory" go run -msan -a msan2.go; then - echo "FAIL: msan2 with -fsanitize=memory" - status=1 - fi - - if ! go run -msan -a msan2.go; then - echo "FAIL: msan2" - status=1 - fi - - if ! go run -msan msan3.go; then - echo "FAIL: msan3" - status=1 - fi - - if ! go run -msan msan4.go; then - echo "FAIL: msan4" - status=1 - fi - - if ! go run -msan msan5.go; then - echo "FAIL: msan5" - status=1 - fi - - if go run -msan msan_fail.go 2>/dev/null; then - echo "FAIL: msan_fail" - status=1 - fi - - testmsanshared -fi - -testtsanshared() { - goos=$(go env GOOS) - suffix="-installsuffix tsan" - libext="so" - if [ "$goos" = "darwin" ]; then - libext="dylib" - fi - go build -buildmode=c-shared $suffix -o ${TMPDIR}/libtsanshared.$libext tsan_shared.go - - echo 'int main() { return 0; }' > ${TMPDIR}/testtsanshared.c - $CC $(go env GOGCCFLAGS) -fsanitize=thread -o ${TMPDIR}/testtsanshared ${TMPDIR}/testtsanshared.c ${TMPDIR}/libtsanshared.$libext - - if ! LD_LIBRARY_PATH=. ${TMPDIR}/testtsanshared; then - echo "FAIL: tsan_shared" - status=1 - fi - rm -f ${TMPDIR}/{testtsanshared,testtsanshared.c,libtsanshared.$libext} -} - -if test "$tsan" = "yes"; then - echo 'int main() { return 0; }' > ${TMPDIR}/testsanitizers$$.c - ok=yes - if ! $CC -fsanitize=thread ${TMPDIR}/testsanitizers$$.c -o ${TMPDIR}/testsanitizers$$ &> ${TMPDIR}/testsanitizers$$.err; then - ok=no - fi - if grep "unrecognized" ${TMPDIR}/testsanitizers$$.err >& /dev/null; then - echo "skipping tsan tests: -fsanitize=thread not supported" - tsan=no - elif test "$ok" != "yes"; then - cat ${TMPDIR}/testsanitizers$$.err - echo "skipping tsan tests: -fsanitizer=thread build failed" - tsan=no - elif ! ${TMPDIR}/testsanitizers$$ 2>&1; then - echo "skipping tsan tests: running tsan program failed" - tsan=no - fi - rm -f ${TMPDIR}/testsanitizers$$* -fi - -# Run a TSAN test. -# $1 test name -# $2 environment variables -# $3 go run args -testtsan() { - err=${TMPDIR}/tsanerr$$.out - if ! env $2 go run $3 $1 2>$err; then - cat $err - echo "FAIL: $1" - status=1 - elif grep -i warning $err >/dev/null 2>&1; then - cat $err - echo "FAIL: $1" - status=1 - fi - rm -f $err -} - -if test "$tsan" = "yes"; then - testtsan tsan.go - testtsan tsan2.go - testtsan tsan3.go - testtsan tsan4.go - testtsan tsan8.go - testtsan tsan9.go - - # These tests are only reliable using clang or GCC version 7 or later. - # Otherwise runtime/cgo/libcgo.h can't tell whether TSAN is in use. - ok=false - clang=false - if ${CC} --version | grep clang >/dev/null 2>&1; then - ok=true - clang=true - else - ver=$($CC -dumpversion) - major=$(echo $ver | sed -e 's/\([0-9]*\).*/\1/') - if test "$major" -lt 7; then - echo "skipping remaining TSAN tests: GCC version $major (older than 7)" - else - ok=true - fi - fi - - if test "$ok" = "true"; then - # These tests require rebuilding os/user with -fsanitize=thread. - testtsan tsan5.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan" - testtsan tsan6.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan" - testtsan tsan7.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan" - - # The remaining tests reportedly hang when built with GCC; issue #21196. - if test "$clang" = "true"; then - testtsan tsan10.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan" - testtsan tsan11.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan" - testtsan tsan12.go "CGO_CFLAGS=-fsanitize=thread CGO_LDFLAGS=-fsanitize=thread" "-installsuffix=tsan" - fi - - testtsanshared - fi -fi - -exit $status diff --git a/libgo/misc/cgo/testsanitizers/tsan_test.go b/libgo/misc/cgo/testsanitizers/tsan_test.go new file mode 100644 index 00000000000..ec4e0033fb4 --- /dev/null +++ b/libgo/misc/cgo/testsanitizers/tsan_test.go @@ -0,0 +1,56 @@ +// Copyright 2017 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 sanitizers_test + +import ( + "strings" + "testing" +) + +func TestTSAN(t *testing.T) { + t.Parallel() + requireOvercommit(t) + config := configure("thread") + config.skipIfCSanitizerBroken(t) + + mustRun(t, config.goCmd("build", "std")) + + cases := []struct { + src string + needsRuntime bool + }{ + {src: "tsan.go"}, + {src: "tsan2.go"}, + {src: "tsan3.go"}, + {src: "tsan4.go"}, + {src: "tsan5.go", needsRuntime: true}, + {src: "tsan6.go", needsRuntime: true}, + {src: "tsan7.go", needsRuntime: true}, + {src: "tsan8.go"}, + {src: "tsan9.go"}, + {src: "tsan10.go", needsRuntime: true}, + {src: "tsan11.go", needsRuntime: true}, + {src: "tsan12.go", needsRuntime: true}, + } + for _, tc := range cases { + tc := tc + name := strings.TrimSuffix(tc.src, ".go") + t.Run(name, func(t *testing.T) { + t.Parallel() + + dir := newTempDir(t) + defer dir.RemoveAll(t) + + outPath := dir.Join(name) + mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src))) + + cmd := hangProneCmd(outPath) + if tc.needsRuntime { + config.skipIfRuntimeIncompatible(t) + } + mustRun(t, cmd) + }) + } +} diff --git a/libgo/misc/cgo/testshared/shared_test.go b/libgo/misc/cgo/testshared/shared_test.go index 9e682a2fb59..f1e8f0605b6 100644 --- a/libgo/misc/cgo/testshared/shared_test.go +++ b/libgo/misc/cgo/testshared/shared_test.go @@ -10,6 +10,7 @@ import ( "debug/elf" "encoding/binary" "errors" + "flag" "fmt" "go/build" "io" @@ -46,7 +47,7 @@ func run(t *testing.T, msg string, args ...string) { func goCmd(t *testing.T, args ...string) { newargs := []string{args[0], "-installsuffix=" + suffix} if testing.Verbose() { - newargs = append(newargs, "-v") + newargs = append(newargs, "-x") } newargs = append(newargs, args[1:]...) c := exec.Command("go", newargs...) @@ -57,6 +58,7 @@ func goCmd(t *testing.T, args ...string) { c.Stdout = os.Stdout c.Stderr = os.Stderr err = c.Run() + output = []byte("(output above)") } else { output, err = c.CombinedOutput() } @@ -161,6 +163,8 @@ func testMain(m *testing.M) (int, error) { } func TestMain(m *testing.M) { + flag.Parse() + // Some of the tests install binaries into a custom GOPATH. // That won't work if GOBIN is set. os.Unsetenv("GOBIN") @@ -461,13 +465,13 @@ func TestGopathShlib(t *testing.T) { // that is not mapped into memory. func testPkgListNote(t *testing.T, f *elf.File, note *note) { if note.section.Flags != 0 { - t.Errorf("package list section has flags %v", note.section.Flags) + t.Errorf("package list section has flags %v, want 0", note.section.Flags) } if isOffsetLoaded(f, note.section.Offset) { t.Errorf("package list section contained in PT_LOAD segment") } if note.desc != "depBase\n" { - t.Errorf("incorrect package list %q", note.desc) + t.Errorf("incorrect package list %q, want %q", note.desc, "depBase\n") } } @@ -476,7 +480,7 @@ func testPkgListNote(t *testing.T, f *elf.File, note *note) { // bytes into it. func testABIHashNote(t *testing.T, f *elf.File, note *note) { if note.section.Flags != elf.SHF_ALLOC { - t.Errorf("abi hash section has flags %v", note.section.Flags) + t.Errorf("abi hash section has flags %v, want SHF_ALLOC", note.section.Flags) } if !isOffsetLoaded(f, note.section.Offset) { t.Errorf("abihash section not contained in PT_LOAD segment") @@ -497,13 +501,13 @@ func testABIHashNote(t *testing.T, f *elf.File, note *note) { return } if elf.ST_BIND(hashbytes.Info) != elf.STB_LOCAL { - t.Errorf("%s has incorrect binding %v", hashbytes.Name, elf.ST_BIND(hashbytes.Info)) + t.Errorf("%s has incorrect binding %v, want STB_LOCAL", hashbytes.Name, elf.ST_BIND(hashbytes.Info)) } if f.Sections[hashbytes.Section] != note.section { - t.Errorf("%s has incorrect section %v", hashbytes.Name, f.Sections[hashbytes.Section].Name) + t.Errorf("%s has incorrect section %v, want %s", hashbytes.Name, f.Sections[hashbytes.Section].Name, note.section.Name) } if hashbytes.Value-note.section.Addr != 16 { - t.Errorf("%s has incorrect offset into section %d", hashbytes.Name, hashbytes.Value-note.section.Addr) + t.Errorf("%s has incorrect offset into section %d, want 16", hashbytes.Name, hashbytes.Value-note.section.Addr) } } @@ -511,14 +515,14 @@ func testABIHashNote(t *testing.T, f *elf.File, note *note) { // was linked against in an unmapped section. func testDepsNote(t *testing.T, f *elf.File, note *note) { if note.section.Flags != 0 { - t.Errorf("package list section has flags %v", note.section.Flags) + t.Errorf("package list section has flags %v, want 0", note.section.Flags) } if isOffsetLoaded(f, note.section.Offset) { t.Errorf("package list section contained in PT_LOAD segment") } // libdepBase.so just links against the lib containing the runtime. if note.desc != soname { - t.Errorf("incorrect dependency list %q", note.desc) + t.Errorf("incorrect dependency list %q, want %q", note.desc, soname) } } @@ -556,7 +560,7 @@ func TestNotes(t *testing.T) { abiHashNoteFound = true case 3: // ELF_NOTE_GODEPS_TAG if depsNoteFound { - t.Error("multiple abi hash notes") + t.Error("multiple depedency list notes") } testDepsNote(t, f, note) depsNoteFound = true @@ -594,6 +598,7 @@ func TestThreeGopathShlibs(t *testing.T) { // If gccgo is not available or not new enough call t.Skip. Otherwise, // return a build.Context that is set up for gccgo. func prepGccgo(t *testing.T) build.Context { + t.Skip("golang.org/issue/22472") gccgoName := os.Getenv("GCCGO") if gccgoName == "" { gccgoName = "gccgo" @@ -643,6 +648,8 @@ func TestGoPathShlibGccgo(t *testing.T) { // library with gccgo, another GOPATH package that depends on the first and an // executable that links the second library. func TestTwoGopathShlibsGccgo(t *testing.T) { + t.Skip("golang.org/issue/22224") + gccgoContext := prepGccgo(t) libgoRE := regexp.MustCompile("libgo.so.[0-9]+") @@ -696,18 +703,55 @@ func resetFileStamps() { reset(gorootInstallDir) } -// touch makes path newer than the "old" time stamp used by resetFileStamps. -func touch(path string) { +// touch changes path and returns a function that changes it back. +// It also sets the time of the file, so that we can see if it is rewritten. +func touch(t *testing.T, path string) (cleanup func()) { + data, err := ioutil.ReadFile(path) + if err != nil { + t.Fatal(err) + } + old := make([]byte, len(data)) + copy(old, data) + if bytes.HasPrefix(data, []byte("!<arch>\n")) { + // Change last digit of build ID. + // (Content ID in the new content-based build IDs.) + const marker = `build id "` + i := bytes.Index(data, []byte(marker)) + if i < 0 { + t.Fatal("cannot find build id in archive") + } + j := bytes.IndexByte(data[i+len(marker):], '"') + if j < 0 { + t.Fatal("cannot find build id in archive") + } + i += len(marker) + j - 1 + if data[i] == 'a' { + data[i] = 'b' + } else { + data[i] = 'a' + } + } else { + // assume it's a text file + data = append(data, '\n') + } + if err := ioutil.WriteFile(path, data, 0666); err != nil { + t.Fatal(err) + } if err := os.Chtimes(path, nearlyNew, nearlyNew); err != nil { - log.Fatalf("os.Chtimes failed: %v", err) + t.Fatal(err) + } + return func() { + if err := ioutil.WriteFile(path, old, 0666); err != nil { + t.Fatal(err) + } } } // isNew returns if the path is newer than the time stamp used by touch. -func isNew(path string) bool { +func isNew(t *testing.T, path string) bool { fi, err := os.Stat(path) if err != nil { - log.Fatalf("os.Stat failed: %v", err) + t.Fatal(err) } return fi.ModTime().After(stampTime) } @@ -715,14 +759,16 @@ func isNew(path string) bool { // Fail unless path has been rebuilt (i.e. is newer than the time stamp used by // isNew) func AssertRebuilt(t *testing.T, msg, path string) { - if !isNew(path) { + t.Helper() + if !isNew(t, path) { t.Errorf("%s was not rebuilt (%s)", msg, path) } } // Fail if path has been rebuilt (i.e. is newer than the time stamp used by isNew) func AssertNotRebuilt(t *testing.T, msg, path string) { - if isNew(path) { + t.Helper() + if isNew(t, path) { t.Errorf("%s was rebuilt (%s)", msg, path) } } @@ -732,41 +778,55 @@ func TestRebuilding(t *testing.T) { goCmd(t, "install", "-linkshared", "exe") // If the source is newer than both the .a file and the .so, both are rebuilt. - resetFileStamps() - touch("src/depBase/dep.go") - goCmd(t, "install", "-linkshared", "exe") - AssertRebuilt(t, "new source", filepath.Join(gopathInstallDir, "depBase.a")) - AssertRebuilt(t, "new source", filepath.Join(gopathInstallDir, "libdepBase.so")) + t.Run("newsource", func(t *testing.T) { + resetFileStamps() + cleanup := touch(t, "src/depBase/dep.go") + defer func() { + cleanup() + goCmd(t, "install", "-linkshared", "exe") + }() + goCmd(t, "install", "-linkshared", "exe") + AssertRebuilt(t, "new source", filepath.Join(gopathInstallDir, "depBase.a")) + AssertRebuilt(t, "new source", filepath.Join(gopathInstallDir, "libdepBase.so")) + }) // If the .a file is newer than the .so, the .so is rebuilt (but not the .a) - resetFileStamps() - touch(filepath.Join(gopathInstallDir, "depBase.a")) - goCmd(t, "install", "-linkshared", "exe") - AssertNotRebuilt(t, "new .a file", filepath.Join(gopathInstallDir, "depBase.a")) - AssertRebuilt(t, "new .a file", filepath.Join(gopathInstallDir, "libdepBase.so")) + t.Run("newarchive", func(t *testing.T) { + resetFileStamps() + goCmd(t, "list", "-linkshared", "-f={{.ImportPath}} {{.Stale}} {{.StaleReason}} {{.Target}}", "depBase") + AssertNotRebuilt(t, "new .a file before build", filepath.Join(gopathInstallDir, "depBase.a")) + cleanup := touch(t, filepath.Join(gopathInstallDir, "depBase.a")) + defer func() { + cleanup() + goCmd(t, "install", "-v", "-linkshared", "exe") + }() + goCmd(t, "install", "-v", "-linkshared", "exe") + AssertNotRebuilt(t, "new .a file", filepath.Join(gopathInstallDir, "depBase.a")) + AssertRebuilt(t, "new .a file", filepath.Join(gopathInstallDir, "libdepBase.so")) + }) } -func appendFile(path, content string) { +func appendFile(t *testing.T, path, content string) { f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0660) if err != nil { - log.Fatalf("os.OpenFile failed: %v", err) + t.Fatalf("os.OpenFile failed: %v", err) } defer func() { err := f.Close() if err != nil { - log.Fatalf("f.Close failed: %v", err) + t.Fatalf("f.Close failed: %v", err) } }() _, err = f.WriteString(content) if err != nil { - log.Fatalf("f.WriteString failed: %v", err) + t.Fatalf("f.WriteString failed: %v", err) } } -func writeFile(path, content string) { +func writeFile(t *testing.T, path, content string) { err := ioutil.WriteFile(path, []byte(content), 0644) if err != nil { - log.Fatalf("ioutil.WriteFile failed: %v", err) + t.Fatalf("ioutil.WriteFile failed: %v", err) } } @@ -780,7 +840,7 @@ func TestABIChecking(t *testing.T) { // some senses but suffices for the narrow definition of ABI compatibility the // toolchain uses today. resetFileStamps() - appendFile("src/depBase/dep.go", "func ABIBreak() {}\n") + appendFile(t, "src/depBase/dep.go", "func ABIBreak() {}\n") goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase") c := exec.Command("./bin/exe") output, err := c.CombinedOutput() @@ -811,7 +871,7 @@ func TestABIChecking(t *testing.T) { // function) and rebuild libdepBase.so, exe still works, even if new function // is in a file by itself. resetFileStamps() - writeFile("src/depBase/dep2.go", "package depBase\nfunc noABIBreak() {}\n") + writeFile(t, "src/depBase/dep2.go", "package depBase\nfunc noABIBreak() {}\n") goCmd(t, "install", "-buildmode=shared", "-linkshared", "depBase") run(t, "after non-ABI breaking change", "./bin/exe") } @@ -838,3 +898,12 @@ func TestInterface(t *testing.T) { goCmd(t, "install", "-linkshared", "iface") run(t, "running type/itab uniqueness tester", "./bin/iface") } + +// Access a global variable from a library. +func TestGlobal(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "globallib") + goCmd(t, "install", "-linkshared", "global") + run(t, "global executable", "./bin/global") + AssertIsLinkedTo(t, "./bin/global", soname) + AssertHasRPath(t, "./bin/global", gorootInstallDir) +} diff --git a/libgo/misc/cgo/testshared/src/depBase/dep.go b/libgo/misc/cgo/testshared/src/depBase/dep.go index 9f86710db01..569c210aa14 100644 --- a/libgo/misc/cgo/testshared/src/depBase/dep.go +++ b/libgo/misc/cgo/testshared/src/depBase/dep.go @@ -22,7 +22,7 @@ type Dep struct { func (d *Dep) Method() int { // This code below causes various go.itab.* symbols to be generated in // the shared library. Similar code in ../exe/exe.go results in - // exercising https://github.com/golang/go/issues/17594 + // exercising https://golang.org/issues/17594 reflect.TypeOf(os.Stdout).Elem() return 10 } diff --git a/libgo/misc/cgo/testshared/src/exe/exe.go b/libgo/misc/cgo/testshared/src/exe/exe.go index 84302a811f0..bd864d88ad8 100644 --- a/libgo/misc/cgo/testshared/src/exe/exe.go +++ b/libgo/misc/cgo/testshared/src/exe/exe.go @@ -25,7 +25,7 @@ func main() { defer depBase.ImplementedInAsm() // This code below causes various go.itab.* symbols to be generated in // the executable. Similar code in ../depBase/dep.go results in - // exercising https://github.com/golang/go/issues/17594 + // exercising https://golang.org/issues/17594 reflect.TypeOf(os.Stdout).Elem() runtime.GC() depBase.V = depBase.F() + 1 diff --git a/libgo/misc/cgo/testshared/src/global/main.go b/libgo/misc/cgo/testshared/src/global/main.go new file mode 100644 index 00000000000..94e7f247dee --- /dev/null +++ b/libgo/misc/cgo/testshared/src/global/main.go @@ -0,0 +1,71 @@ +// Copyright 2017 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 main + +import ( + "globallib" +) + +//go:noinline +func testLoop() { + for i, s := range globallib.Data { + if s != int64(i) { + panic("testLoop: mismatch") + } + } +} + +//go:noinline +func ptrData() *[1<<20 + 10]int64 { + return &globallib.Data +} + +//go:noinline +func testMediumOffset() { + for i, s := range globallib.Data[1<<16-2:] { + if s != int64(i)+1<<16-2 { + panic("testMediumOffset: index mismatch") + } + } + + x := globallib.Data[1<<16-1] + if x != 1<<16-1 { + panic("testMediumOffset: direct mismatch") + } + + y := &globallib.Data[1<<16-3] + if y != &ptrData()[1<<16-3] { + panic("testMediumOffset: address mismatch") + } +} + +//go:noinline +func testLargeOffset() { + for i, s := range globallib.Data[1<<20:] { + if s != int64(i)+1<<20 { + panic("testLargeOffset: index mismatch") + } + } + + x := globallib.Data[1<<20+1] + if x != 1<<20+1 { + panic("testLargeOffset: direct mismatch") + } + + y := &globallib.Data[1<<20+2] + if y != &ptrData()[1<<20+2] { + panic("testLargeOffset: address mismatch") + } +} + +func main() { + testLoop() + + // SSA rules commonly merge offsets into addresses. These + // tests access global data in different ways to try + // and exercise different SSA rules. + testMediumOffset() + testLargeOffset() +} diff --git a/libgo/misc/cgo/testshared/src/globallib/global.go b/libgo/misc/cgo/testshared/src/globallib/global.go new file mode 100644 index 00000000000..b4372a2e9e2 --- /dev/null +++ b/libgo/misc/cgo/testshared/src/globallib/global.go @@ -0,0 +1,17 @@ +// Copyright 2017 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 globallib + +// Data is large enough to that offsets into it do not fit into +// 16-bit or 20-bit immediates. Ideally we'd also try and overrun +// 32-bit immediates, but that requires the test machine to have +// too much memory. +var Data [1<<20 + 10]int64 + +func init() { + for i := range Data { + Data[i] = int64(i) + } +} |