github.com/c9s/go@v0.0.0-20180120015821-984e81f64e0c/src/cmd/nm/nm_test.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"fmt"
    11  	"internal/testenv"
    12  	"io/ioutil"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"runtime"
    17  	"strings"
    18  	"testing"
    19  	"text/template"
    20  )
    21  
    22  var testnmpath string // path to nm command created for testing purposes
    23  
    24  // The TestMain function creates a nm command for testing purposes and
    25  // deletes it after the tests have been run.
    26  func TestMain(m *testing.M) {
    27  	os.Exit(testMain(m))
    28  }
    29  
    30  func testMain(m *testing.M) int {
    31  	if !testenv.HasGoBuild() {
    32  		return 0
    33  	}
    34  
    35  	tmpDir, err := ioutil.TempDir("", "TestNM")
    36  	if err != nil {
    37  		fmt.Println("TempDir failed:", err)
    38  		return 2
    39  	}
    40  	defer os.RemoveAll(tmpDir)
    41  
    42  	testnmpath = filepath.Join(tmpDir, "testnm.exe")
    43  	gotool, err := testenv.GoTool()
    44  	if err != nil {
    45  		fmt.Println("GoTool failed:", err)
    46  		return 2
    47  	}
    48  	out, err := exec.Command(gotool, "build", "-o", testnmpath, "cmd/nm").CombinedOutput()
    49  	if err != nil {
    50  		fmt.Printf("go build -o %v cmd/nm: %v\n%s", testnmpath, err, string(out))
    51  		return 2
    52  	}
    53  
    54  	return m.Run()
    55  }
    56  
    57  func TestNonGoExecs(t *testing.T) {
    58  	testfiles := []string{
    59  		"elf/testdata/gcc-386-freebsd-exec",
    60  		"elf/testdata/gcc-amd64-linux-exec",
    61  		"macho/testdata/gcc-386-darwin-exec",
    62  		"macho/testdata/gcc-amd64-darwin-exec",
    63  		// "pe/testdata/gcc-amd64-mingw-exec", // no symbols!
    64  		"pe/testdata/gcc-386-mingw-exec",
    65  		"plan9obj/testdata/amd64-plan9-exec",
    66  		"plan9obj/testdata/386-plan9-exec",
    67  	}
    68  	for _, f := range testfiles {
    69  		exepath := filepath.Join(runtime.GOROOT(), "src", "debug", f)
    70  		cmd := exec.Command(testnmpath, exepath)
    71  		out, err := cmd.CombinedOutput()
    72  		if err != nil {
    73  			t.Errorf("go tool nm %v: %v\n%s", exepath, err, string(out))
    74  		}
    75  	}
    76  }
    77  
    78  func testGoExec(t *testing.T, iscgo, isexternallinker bool) {
    79  	tmpdir, err := ioutil.TempDir("", "TestGoExec")
    80  	if err != nil {
    81  		t.Fatal(err)
    82  	}
    83  	defer os.RemoveAll(tmpdir)
    84  
    85  	src := filepath.Join(tmpdir, "a.go")
    86  	file, err := os.Create(src)
    87  	if err != nil {
    88  		t.Fatal(err)
    89  	}
    90  	err = template.Must(template.New("main").Parse(testexec)).Execute(file, iscgo)
    91  	if e := file.Close(); err == nil {
    92  		err = e
    93  	}
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  
    98  	exe := filepath.Join(tmpdir, "a.exe")
    99  	args := []string{"build", "-o", exe}
   100  	if iscgo {
   101  		linkmode := "internal"
   102  		if isexternallinker {
   103  			linkmode = "external"
   104  		}
   105  		args = append(args, "-ldflags", "-linkmode="+linkmode)
   106  	}
   107  	args = append(args, src)
   108  	out, err := exec.Command(testenv.GoToolPath(t), args...).CombinedOutput()
   109  	if err != nil {
   110  		t.Fatalf("building test executable failed: %s %s", err, out)
   111  	}
   112  
   113  	out, err = exec.Command(exe).CombinedOutput()
   114  	if err != nil {
   115  		t.Fatalf("running test executable failed: %s %s", err, out)
   116  	}
   117  	names := make(map[string]string)
   118  	for _, line := range strings.Split(string(out), "\n") {
   119  		if line == "" {
   120  			continue
   121  		}
   122  		f := strings.Split(line, "=")
   123  		if len(f) != 2 {
   124  			t.Fatalf("unexpected output line: %q", line)
   125  		}
   126  		names["main."+f[0]] = f[1]
   127  	}
   128  
   129  	out, err = exec.Command(testnmpath, exe).CombinedOutput()
   130  	if err != nil {
   131  		t.Fatalf("go tool nm: %v\n%s", err, string(out))
   132  	}
   133  	scanner := bufio.NewScanner(bytes.NewBuffer(out))
   134  	dups := make(map[string]bool)
   135  	for scanner.Scan() {
   136  		f := strings.Fields(scanner.Text())
   137  		if len(f) < 3 {
   138  			continue
   139  		}
   140  		name := f[2]
   141  		if addr, found := names[name]; found {
   142  			if want, have := addr, "0x"+f[0]; have != want {
   143  				t.Errorf("want %s address for %s symbol, but have %s", want, name, have)
   144  			}
   145  			delete(names, name)
   146  		}
   147  		if _, found := dups[name]; found {
   148  			t.Errorf("duplicate name of %q is found", name)
   149  		}
   150  	}
   151  	err = scanner.Err()
   152  	if err != nil {
   153  		t.Fatalf("error reading nm output: %v", err)
   154  	}
   155  	if len(names) > 0 {
   156  		t.Errorf("executable is missing %v symbols", names)
   157  	}
   158  }
   159  
   160  func TestGoExec(t *testing.T) {
   161  	testGoExec(t, false, false)
   162  }
   163  
   164  func testGoLib(t *testing.T, iscgo bool) {
   165  	tmpdir, err := ioutil.TempDir("", "TestGoLib")
   166  	if err != nil {
   167  		t.Fatal(err)
   168  	}
   169  	defer os.RemoveAll(tmpdir)
   170  
   171  	gopath := filepath.Join(tmpdir, "gopath")
   172  	libpath := filepath.Join(gopath, "src", "mylib")
   173  
   174  	err = os.MkdirAll(libpath, 0777)
   175  	if err != nil {
   176  		t.Fatal(err)
   177  	}
   178  	src := filepath.Join(libpath, "a.go")
   179  	file, err := os.Create(src)
   180  	if err != nil {
   181  		t.Fatal(err)
   182  	}
   183  	err = template.Must(template.New("mylib").Parse(testlib)).Execute(file, iscgo)
   184  	if e := file.Close(); err == nil {
   185  		err = e
   186  	}
   187  	if err != nil {
   188  		t.Fatal(err)
   189  	}
   190  
   191  	args := []string{"install", "mylib"}
   192  	cmd := exec.Command(testenv.GoToolPath(t), args...)
   193  	cmd.Env = append(os.Environ(), "GOPATH="+gopath)
   194  	out, err := cmd.CombinedOutput()
   195  	if err != nil {
   196  		t.Fatalf("building test lib failed: %s %s", err, out)
   197  	}
   198  	pat := filepath.Join(gopath, "pkg", "*", "mylib.a")
   199  	ms, err := filepath.Glob(pat)
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	if len(ms) == 0 {
   204  		t.Fatalf("cannot found paths for pattern %s", pat)
   205  	}
   206  	mylib := ms[0]
   207  
   208  	out, err = exec.Command(testnmpath, mylib).CombinedOutput()
   209  	if err != nil {
   210  		t.Fatalf("go tool nm: %v\n%s", err, string(out))
   211  	}
   212  	type symType struct {
   213  		Type  string
   214  		Name  string
   215  		CSym  bool
   216  		Found bool
   217  	}
   218  	var syms = []symType{
   219  		{"B", "%22%22.Testdata", false, false},
   220  		{"T", "%22%22.Testfunc", false, false},
   221  	}
   222  	if iscgo {
   223  		syms = append(syms, symType{"B", "%22%22.TestCgodata", false, false})
   224  		syms = append(syms, symType{"T", "%22%22.TestCgofunc", false, false})
   225  		if runtime.GOOS == "darwin" || (runtime.GOOS == "windows" && runtime.GOARCH == "386") {
   226  			syms = append(syms, symType{"D", "_cgodata", true, false})
   227  			syms = append(syms, symType{"T", "_cgofunc", true, false})
   228  		} else {
   229  			syms = append(syms, symType{"D", "cgodata", true, false})
   230  			syms = append(syms, symType{"T", "cgofunc", true, false})
   231  		}
   232  	}
   233  	scanner := bufio.NewScanner(bytes.NewBuffer(out))
   234  	for scanner.Scan() {
   235  		f := strings.Fields(scanner.Text())
   236  		var typ, name string
   237  		var csym bool
   238  		if iscgo {
   239  			if len(f) < 4 {
   240  				continue
   241  			}
   242  			csym = !strings.Contains(f[0], "_go_.o")
   243  			typ = f[2]
   244  			name = f[3]
   245  		} else {
   246  			if len(f) < 3 {
   247  				continue
   248  			}
   249  			typ = f[1]
   250  			name = f[2]
   251  		}
   252  		for i := range syms {
   253  			sym := &syms[i]
   254  			if sym.Type == typ && sym.Name == name && sym.CSym == csym {
   255  				if sym.Found {
   256  					t.Fatalf("duplicate symbol %s %s", sym.Type, sym.Name)
   257  				}
   258  				sym.Found = true
   259  			}
   260  		}
   261  	}
   262  	err = scanner.Err()
   263  	if err != nil {
   264  		t.Fatalf("error reading nm output: %v", err)
   265  	}
   266  	for _, sym := range syms {
   267  		if !sym.Found {
   268  			t.Errorf("cannot found symbol %s %s", sym.Type, sym.Name)
   269  		}
   270  	}
   271  }
   272  
   273  func TestGoLib(t *testing.T) {
   274  	testGoLib(t, false)
   275  }
   276  
   277  const testexec = `
   278  package main
   279  
   280  import "fmt"
   281  {{if .}}import "C"
   282  {{end}}
   283  
   284  func main() {
   285  	testfunc()
   286  }
   287  
   288  var testdata uint32
   289  
   290  func testfunc() {
   291  	fmt.Printf("main=%p\n", main)
   292  	fmt.Printf("testfunc=%p\n", testfunc)
   293  	fmt.Printf("testdata=%p\n", &testdata)
   294  }
   295  `
   296  
   297  const testlib = `
   298  package mylib
   299  
   300  {{if .}}
   301  // int cgodata = 5;
   302  // void cgofunc(void) {}
   303  import "C"
   304  
   305  var TestCgodata = C.cgodata
   306  
   307  func TestCgofunc() {
   308  	C.cgofunc()
   309  }
   310  {{end}}
   311  
   312  var Testdata uint32
   313  
   314  func Testfunc() {}
   315  `