mirror of https://github.com/golang/go.git
376 lines
8.7 KiB
Go
376 lines
8.7 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"internal/obscuretestdata"
|
|
"internal/testenv"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
"text/template"
|
|
)
|
|
|
|
var testnmpath string // path to nm command created for testing purposes
|
|
|
|
// The TestMain function creates a nm command for testing purposes and
|
|
// deletes it after the tests have been run.
|
|
func TestMain(m *testing.M) {
|
|
os.Exit(testMain(m))
|
|
}
|
|
|
|
func testMain(m *testing.M) int {
|
|
if !testenv.HasGoBuild() {
|
|
return 0
|
|
}
|
|
|
|
tmpDir, err := ioutil.TempDir("", "TestNM")
|
|
if err != nil {
|
|
fmt.Println("TempDir failed:", err)
|
|
return 2
|
|
}
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
testnmpath = filepath.Join(tmpDir, "testnm.exe")
|
|
gotool, err := testenv.GoTool()
|
|
if err != nil {
|
|
fmt.Println("GoTool failed:", err)
|
|
return 2
|
|
}
|
|
out, err := exec.Command(gotool, "build", "-o", testnmpath, "cmd/nm").CombinedOutput()
|
|
if err != nil {
|
|
fmt.Printf("go build -o %v cmd/nm: %v\n%s", testnmpath, err, string(out))
|
|
return 2
|
|
}
|
|
|
|
return m.Run()
|
|
}
|
|
|
|
func TestNonGoExecs(t *testing.T) {
|
|
t.Parallel()
|
|
testfiles := []string{
|
|
"debug/elf/testdata/gcc-386-freebsd-exec",
|
|
"debug/elf/testdata/gcc-amd64-linux-exec",
|
|
"debug/macho/testdata/gcc-386-darwin-exec.base64", // golang.org/issue/34986
|
|
"debug/macho/testdata/gcc-amd64-darwin-exec.base64", // golang.org/issue/34986
|
|
// "debug/pe/testdata/gcc-amd64-mingw-exec", // no symbols!
|
|
"debug/pe/testdata/gcc-386-mingw-exec",
|
|
"debug/plan9obj/testdata/amd64-plan9-exec",
|
|
"debug/plan9obj/testdata/386-plan9-exec",
|
|
"internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec",
|
|
}
|
|
for _, f := range testfiles {
|
|
exepath := filepath.Join(runtime.GOROOT(), "src", f)
|
|
if strings.HasSuffix(f, ".base64") {
|
|
tf, err := obscuretestdata.DecodeToTempFile(exepath)
|
|
if err != nil {
|
|
t.Errorf("obscuretestdata.DecodeToTempFile(%s): %v", exepath, err)
|
|
continue
|
|
}
|
|
defer os.Remove(tf)
|
|
exepath = tf
|
|
}
|
|
|
|
cmd := exec.Command(testnmpath, exepath)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Errorf("go tool nm %v: %v\n%s", exepath, err, string(out))
|
|
}
|
|
}
|
|
}
|
|
|
|
func testGoExec(t *testing.T, iscgo, isexternallinker bool) {
|
|
t.Parallel()
|
|
tmpdir, err := ioutil.TempDir("", "TestGoExec")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
src := filepath.Join(tmpdir, "a.go")
|
|
file, err := os.Create(src)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = template.Must(template.New("main").Parse(testexec)).Execute(file, iscgo)
|
|
if e := file.Close(); err == nil {
|
|
err = e
|
|
}
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
exe := filepath.Join(tmpdir, "a.exe")
|
|
args := []string{"build", "-o", exe}
|
|
if iscgo {
|
|
linkmode := "internal"
|
|
if isexternallinker {
|
|
linkmode = "external"
|
|
}
|
|
args = append(args, "-ldflags", "-linkmode="+linkmode)
|
|
}
|
|
args = append(args, src)
|
|
out, err := exec.Command(testenv.GoToolPath(t), args...).CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("building test executable failed: %s %s", err, out)
|
|
}
|
|
|
|
out, err = exec.Command(exe).CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("running test executable failed: %s %s", err, out)
|
|
}
|
|
names := make(map[string]string)
|
|
for _, line := range strings.Split(string(out), "\n") {
|
|
if line == "" {
|
|
continue
|
|
}
|
|
f := strings.Split(line, "=")
|
|
if len(f) != 2 {
|
|
t.Fatalf("unexpected output line: %q", line)
|
|
}
|
|
names["main."+f[0]] = f[1]
|
|
}
|
|
|
|
runtimeSyms := map[string]string{
|
|
"runtime.text": "T",
|
|
"runtime.etext": "T",
|
|
"runtime.rodata": "R",
|
|
"runtime.erodata": "R",
|
|
"runtime.epclntab": "R",
|
|
"runtime.noptrdata": "D",
|
|
}
|
|
|
|
if runtime.GOOS == "aix" && iscgo {
|
|
// pclntab is moved to .data section on AIX.
|
|
runtimeSyms["runtime.epclntab"] = "D"
|
|
}
|
|
|
|
out, err = exec.Command(testnmpath, exe).CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("go tool nm: %v\n%s", err, string(out))
|
|
}
|
|
|
|
relocated := func(code string) bool {
|
|
if runtime.GOOS == "aix" {
|
|
// On AIX, .data and .bss addresses are changed by the loader.
|
|
// Therefore, the values returned by the exec aren't the same
|
|
// than the ones inside the symbol table.
|
|
// In case of cgo, .text symbols are also changed.
|
|
switch code {
|
|
case "T", "t", "R", "r":
|
|
return iscgo
|
|
case "D", "d", "B", "b":
|
|
return true
|
|
}
|
|
}
|
|
if runtime.GOOS == "windows" {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
dups := make(map[string]bool)
|
|
for _, line := range strings.Split(string(out), "\n") {
|
|
f := strings.Fields(line)
|
|
if len(f) < 3 {
|
|
continue
|
|
}
|
|
name := f[2]
|
|
if addr, found := names[name]; found {
|
|
if want, have := addr, "0x"+f[0]; have != want {
|
|
if !relocated(f[1]) {
|
|
t.Errorf("want %s address for %s symbol, but have %s", want, name, have)
|
|
}
|
|
}
|
|
delete(names, name)
|
|
}
|
|
if _, found := dups[name]; found {
|
|
t.Errorf("duplicate name of %q is found", name)
|
|
}
|
|
if stype, found := runtimeSyms[name]; found {
|
|
if runtime.GOOS == "plan9" && stype == "R" {
|
|
// no read-only data segment symbol on Plan 9
|
|
stype = "D"
|
|
}
|
|
if want, have := stype, strings.ToUpper(f[1]); have != want {
|
|
t.Errorf("want %s type for %s symbol, but have %s", want, name, have)
|
|
}
|
|
delete(runtimeSyms, name)
|
|
}
|
|
}
|
|
if len(names) > 0 {
|
|
t.Errorf("executable is missing %v symbols", names)
|
|
}
|
|
if len(runtimeSyms) > 0 {
|
|
t.Errorf("executable is missing %v symbols", runtimeSyms)
|
|
}
|
|
}
|
|
|
|
func TestGoExec(t *testing.T) {
|
|
testGoExec(t, false, false)
|
|
}
|
|
|
|
func testGoLib(t *testing.T, iscgo bool) {
|
|
t.Parallel()
|
|
tmpdir, err := ioutil.TempDir("", "TestGoLib")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
gopath := filepath.Join(tmpdir, "gopath")
|
|
libpath := filepath.Join(gopath, "src", "mylib")
|
|
|
|
err = os.MkdirAll(libpath, 0777)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
src := filepath.Join(libpath, "a.go")
|
|
file, err := os.Create(src)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err = template.Must(template.New("mylib").Parse(testlib)).Execute(file, iscgo)
|
|
if e := file.Close(); err == nil {
|
|
err = e
|
|
}
|
|
if err == nil {
|
|
err = ioutil.WriteFile(filepath.Join(libpath, "go.mod"), []byte("module mylib\n"), 0666)
|
|
}
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
args := []string{"install", "mylib"}
|
|
cmd := exec.Command(testenv.GoToolPath(t), args...)
|
|
cmd.Dir = libpath
|
|
cmd.Env = append(os.Environ(), "GOPATH="+gopath)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("building test lib failed: %s %s", err, out)
|
|
}
|
|
pat := filepath.Join(gopath, "pkg", "*", "mylib.a")
|
|
ms, err := filepath.Glob(pat)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(ms) == 0 {
|
|
t.Fatalf("cannot found paths for pattern %s", pat)
|
|
}
|
|
mylib := ms[0]
|
|
|
|
out, err = exec.Command(testnmpath, mylib).CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("go tool nm: %v\n%s", err, string(out))
|
|
}
|
|
type symType struct {
|
|
Type string
|
|
Name string
|
|
CSym bool
|
|
Found bool
|
|
}
|
|
var syms = []symType{
|
|
{"B", "mylib.Testdata", false, false},
|
|
{"T", "mylib.Testfunc", false, false},
|
|
}
|
|
if iscgo {
|
|
syms = append(syms, symType{"B", "mylib.TestCgodata", false, false})
|
|
syms = append(syms, symType{"T", "mylib.TestCgofunc", false, false})
|
|
if runtime.GOOS == "darwin" || runtime.GOOS == "ios" || (runtime.GOOS == "windows" && runtime.GOARCH == "386") {
|
|
syms = append(syms, symType{"D", "_cgodata", true, false})
|
|
syms = append(syms, symType{"T", "_cgofunc", true, false})
|
|
} else if runtime.GOOS == "aix" {
|
|
syms = append(syms, symType{"D", "cgodata", true, false})
|
|
syms = append(syms, symType{"T", ".cgofunc", true, false})
|
|
} else {
|
|
syms = append(syms, symType{"D", "cgodata", true, false})
|
|
syms = append(syms, symType{"T", "cgofunc", true, false})
|
|
}
|
|
}
|
|
|
|
for _, line := range strings.Split(string(out), "\n") {
|
|
f := strings.Fields(line)
|
|
var typ, name string
|
|
var csym bool
|
|
if iscgo {
|
|
if len(f) < 4 {
|
|
continue
|
|
}
|
|
csym = !strings.Contains(f[0], "_go_.o")
|
|
typ = f[2]
|
|
name = f[3]
|
|
} else {
|
|
if len(f) < 3 {
|
|
continue
|
|
}
|
|
typ = f[1]
|
|
name = f[2]
|
|
}
|
|
for i := range syms {
|
|
sym := &syms[i]
|
|
if sym.Type == typ && sym.Name == name && sym.CSym == csym {
|
|
if sym.Found {
|
|
t.Fatalf("duplicate symbol %s %s", sym.Type, sym.Name)
|
|
}
|
|
sym.Found = true
|
|
}
|
|
}
|
|
}
|
|
for _, sym := range syms {
|
|
if !sym.Found {
|
|
t.Errorf("cannot found symbol %s %s", sym.Type, sym.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGoLib(t *testing.T) {
|
|
testGoLib(t, false)
|
|
}
|
|
|
|
const testexec = `
|
|
package main
|
|
|
|
import "fmt"
|
|
{{if .}}import "C"
|
|
{{end}}
|
|
|
|
func main() {
|
|
testfunc()
|
|
}
|
|
|
|
var testdata uint32
|
|
|
|
func testfunc() {
|
|
fmt.Printf("main=%p\n", main)
|
|
fmt.Printf("testfunc=%p\n", testfunc)
|
|
fmt.Printf("testdata=%p\n", &testdata)
|
|
}
|
|
`
|
|
|
|
const testlib = `
|
|
package mylib
|
|
|
|
{{if .}}
|
|
// int cgodata = 5;
|
|
// void cgofunc(void) {}
|
|
import "C"
|
|
|
|
var TestCgodata = C.cgodata
|
|
|
|
func TestCgofunc() {
|
|
C.cgofunc()
|
|
}
|
|
{{end}}
|
|
|
|
var Testdata uint32
|
|
|
|
func Testfunc() {}
|
|
`
|