mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
misc/cgo: move easy tests to cmd/cgo/internal
This moves most misc/cgo tests to cmd/cgo/internal. This is mostly a trivial rename and updating dist/test.go for the new paths, plus excluding these packages from regular dist test registration. A few tests were sensitive to what path they ran in, so we update those. This will let these tests access facilities in internal/testenv. For #37486. Change-Id: I3ed417c7c22d9b667f2767c0cb1f59118fcd4af6 Reviewed-on: https://go-review.googlesource.com/c/go/+/492720 Run-TryBot: Austin Clements <austin@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Bryan Mills <bcmills@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
a25688d406
commit
bf6c55a8b3
251 changed files with 42 additions and 28 deletions
219
src/cmd/cgo/internal/testerrors/badsym_test.go
Normal file
219
src/cmd/cgo/internal/testerrors/badsym_test.go
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
// Copyright 2020 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"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// A manually modified object file could pass unexpected characters
|
||||
// into the files generated by cgo.
|
||||
|
||||
const magicInput = "abcdefghijklmnopqrstuvwxyz0123"
|
||||
const magicReplace = "\n//go:cgo_ldflag \"-badflag\"\n//"
|
||||
|
||||
const cSymbol = "BadSymbol" + magicInput + "Name"
|
||||
const cDefSource = "int " + cSymbol + " = 1;"
|
||||
const cRefSource = "extern int " + cSymbol + "; int F() { return " + cSymbol + "; }"
|
||||
|
||||
// goSource is the source code for the trivial Go file we use.
|
||||
// We will replace TMPDIR with the temporary directory name.
|
||||
const goSource = `
|
||||
package main
|
||||
|
||||
// #cgo LDFLAGS: TMPDIR/cbad.o TMPDIR/cbad.so
|
||||
// extern int F();
|
||||
import "C"
|
||||
|
||||
func main() {
|
||||
println(C.F())
|
||||
}
|
||||
`
|
||||
|
||||
func TestBadSymbol(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
mkdir := func(base string) string {
|
||||
ret := filepath.Join(dir, base)
|
||||
if err := os.Mkdir(ret, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
cdir := mkdir("c")
|
||||
godir := mkdir("go")
|
||||
|
||||
makeFile := func(mdir, base, source string) string {
|
||||
ret := filepath.Join(mdir, base)
|
||||
if err := os.WriteFile(ret, []byte(source), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
cDefFile := makeFile(cdir, "cdef.c", cDefSource)
|
||||
cRefFile := makeFile(cdir, "cref.c", cRefSource)
|
||||
|
||||
ccCmd := cCompilerCmd(t)
|
||||
|
||||
cCompile := func(arg, base, src string) string {
|
||||
out := filepath.Join(cdir, base)
|
||||
run := append(ccCmd, arg, "-o", out, src)
|
||||
output, err := exec.Command(run[0], run[1:]...).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Log(run)
|
||||
t.Logf("%s", output)
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Remove(src); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Build a shared library that defines a symbol whose name
|
||||
// contains magicInput.
|
||||
|
||||
cShared := cCompile("-shared", "c.so", cDefFile)
|
||||
|
||||
// Build an object file that refers to the symbol whose name
|
||||
// contains magicInput.
|
||||
|
||||
cObj := cCompile("-c", "c.o", cRefFile)
|
||||
|
||||
// Rewrite the shared library and the object file, replacing
|
||||
// magicInput with magicReplace. This will have the effect of
|
||||
// introducing a symbol whose name looks like a cgo command.
|
||||
// The cgo tool will use that name when it generates the
|
||||
// _cgo_import.go file, thus smuggling a magic //go:cgo_ldflag
|
||||
// pragma into a Go file. We used to not check the pragmas in
|
||||
// _cgo_import.go.
|
||||
|
||||
rewrite := func(from, to string) {
|
||||
obj, err := os.ReadFile(from)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if bytes.Count(obj, []byte(magicInput)) == 0 {
|
||||
t.Fatalf("%s: did not find magic string", from)
|
||||
}
|
||||
|
||||
if len(magicInput) != len(magicReplace) {
|
||||
t.Fatalf("internal test error: different magic lengths: %d != %d", len(magicInput), len(magicReplace))
|
||||
}
|
||||
|
||||
obj = bytes.ReplaceAll(obj, []byte(magicInput), []byte(magicReplace))
|
||||
|
||||
if err := os.WriteFile(to, obj, 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
cBadShared := filepath.Join(godir, "cbad.so")
|
||||
rewrite(cShared, cBadShared)
|
||||
|
||||
cBadObj := filepath.Join(godir, "cbad.o")
|
||||
rewrite(cObj, cBadObj)
|
||||
|
||||
goSourceBadObject := strings.ReplaceAll(goSource, "TMPDIR", godir)
|
||||
makeFile(godir, "go.go", goSourceBadObject)
|
||||
|
||||
makeFile(godir, "go.mod", "module badsym")
|
||||
|
||||
// Try to build our little package.
|
||||
cmd := exec.Command("go", "build", "-ldflags=-v")
|
||||
cmd.Dir = godir
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
// The build should fail, but we want it to fail because we
|
||||
// detected the error, not because we passed a bad flag to the
|
||||
// C linker.
|
||||
|
||||
if err == nil {
|
||||
t.Errorf("go build succeeded unexpectedly")
|
||||
}
|
||||
|
||||
t.Logf("%s", output)
|
||||
|
||||
for _, line := range bytes.Split(output, []byte("\n")) {
|
||||
if bytes.Contains(line, []byte("dynamic symbol")) && bytes.Contains(line, []byte("contains unsupported character")) {
|
||||
// This is the error from cgo.
|
||||
continue
|
||||
}
|
||||
|
||||
// We passed -ldflags=-v to see the external linker invocation,
|
||||
// which should not include -badflag.
|
||||
if bytes.Contains(line, []byte("-badflag")) {
|
||||
t.Error("output should not mention -badflag")
|
||||
}
|
||||
|
||||
// Also check for compiler errors, just in case.
|
||||
// GCC says "unrecognized command line option".
|
||||
// clang says "unknown argument".
|
||||
if bytes.Contains(line, []byte("unrecognized")) || bytes.Contains(output, []byte("unknown")) {
|
||||
t.Error("problem should have been caught before invoking C linker")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cCompilerCmd(t *testing.T) []string {
|
||||
cc := []string{goEnv(t, "CC")}
|
||||
|
||||
out := goEnv(t, "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:])
|
||||
}
|
||||
|
||||
// Force reallocation (and avoid aliasing bugs) for tests that append to cc.
|
||||
cc = cc[:len(cc):len(cc)]
|
||||
|
||||
return cc
|
||||
}
|
||||
|
||||
func goEnv(t *testing.T, key string) string {
|
||||
out, err := exec.Command("go", "env", key).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Logf("go env %s\n", key)
|
||||
t.Logf("%s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue