...
Run Format

Source file src/cmd/nm/nm_test.go

Documentation: cmd/nm

     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  	runtimeSyms := map[string]string{
   130  		"runtime.text":      "T",
   131  		"runtime.etext":     "T",
   132  		"runtime.rodata":    "R",
   133  		"runtime.erodata":   "R",
   134  		"runtime.epclntab":  "R",
   135  		"runtime.noptrdata": "D",
   136  	}
   137  
   138  	out, err = exec.Command(testnmpath, exe).CombinedOutput()
   139  	if err != nil {
   140  		t.Fatalf("go tool nm: %v\n%s", err, string(out))
   141  	}
   142  	scanner := bufio.NewScanner(bytes.NewBuffer(out))
   143  	dups := make(map[string]bool)
   144  	for scanner.Scan() {
   145  		f := strings.Fields(scanner.Text())
   146  		if len(f) < 3 {
   147  			continue
   148  		}
   149  		name := f[2]
   150  		if addr, found := names[name]; found {
   151  			if want, have := addr, "0x"+f[0]; have != want {
   152  				t.Errorf("want %s address for %s symbol, but have %s", want, name, have)
   153  			}
   154  			delete(names, name)
   155  		}
   156  		if _, found := dups[name]; found {
   157  			t.Errorf("duplicate name of %q is found", name)
   158  		}
   159  		if stype, found := runtimeSyms[name]; found {
   160  			if runtime.GOOS == "plan9" && stype == "R" {
   161  				// no read-only data segment symbol on Plan 9
   162  				stype = "D"
   163  			}
   164  			if want, have := stype, strings.ToUpper(f[1]); have != want {
   165  				t.Errorf("want %s type for %s symbol, but have %s", want, name, have)
   166  			}
   167  			delete(runtimeSyms, name)
   168  		}
   169  	}
   170  	err = scanner.Err()
   171  	if err != nil {
   172  		t.Fatalf("error reading nm output: %v", err)
   173  	}
   174  	if len(names) > 0 {
   175  		t.Errorf("executable is missing %v symbols", names)
   176  	}
   177  	if len(runtimeSyms) > 0 {
   178  		t.Errorf("executable is missing %v symbols", runtimeSyms)
   179  	}
   180  }
   181  
   182  func TestGoExec(t *testing.T) {
   183  	testGoExec(t, false, false)
   184  }
   185  
   186  func testGoLib(t *testing.T, iscgo bool) {
   187  	tmpdir, err := ioutil.TempDir("", "TestGoLib")
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  	defer os.RemoveAll(tmpdir)
   192  
   193  	gopath := filepath.Join(tmpdir, "gopath")
   194  	libpath := filepath.Join(gopath, "src", "mylib")
   195  
   196  	err = os.MkdirAll(libpath, 0777)
   197  	if err != nil {
   198  		t.Fatal(err)
   199  	}
   200  	src := filepath.Join(libpath, "a.go")
   201  	file, err := os.Create(src)
   202  	if err != nil {
   203  		t.Fatal(err)
   204  	}
   205  	err = template.Must(template.New("mylib").Parse(testlib)).Execute(file, iscgo)
   206  	if e := file.Close(); err == nil {
   207  		err = e
   208  	}
   209  	if err != nil {
   210  		t.Fatal(err)
   211  	}
   212  
   213  	args := []string{"install", "mylib"}
   214  	cmd := exec.Command(testenv.GoToolPath(t), args...)
   215  	cmd.Env = append(os.Environ(), "GOPATH="+gopath)
   216  	out, err := cmd.CombinedOutput()
   217  	if err != nil {
   218  		t.Fatalf("building test lib failed: %s %s", err, out)
   219  	}
   220  	pat := filepath.Join(gopath, "pkg", "*", "mylib.a")
   221  	ms, err := filepath.Glob(pat)
   222  	if err != nil {
   223  		t.Fatal(err)
   224  	}
   225  	if len(ms) == 0 {
   226  		t.Fatalf("cannot found paths for pattern %s", pat)
   227  	}
   228  	mylib := ms[0]
   229  
   230  	out, err = exec.Command(testnmpath, mylib).CombinedOutput()
   231  	if err != nil {
   232  		t.Fatalf("go tool nm: %v\n%s", err, string(out))
   233  	}
   234  	type symType struct {
   235  		Type  string
   236  		Name  string
   237  		CSym  bool
   238  		Found bool
   239  	}
   240  	var syms = []symType{
   241  		{"B", "%22%22.Testdata", false, false},
   242  		{"T", "%22%22.Testfunc", false, false},
   243  	}
   244  	if iscgo {
   245  		syms = append(syms, symType{"B", "%22%22.TestCgodata", false, false})
   246  		syms = append(syms, symType{"T", "%22%22.TestCgofunc", false, false})
   247  		if runtime.GOOS == "darwin" || (runtime.GOOS == "windows" && runtime.GOARCH == "386") {
   248  			syms = append(syms, symType{"D", "_cgodata", true, false})
   249  			syms = append(syms, symType{"T", "_cgofunc", true, false})
   250  		} else {
   251  			syms = append(syms, symType{"D", "cgodata", true, false})
   252  			syms = append(syms, symType{"T", "cgofunc", true, false})
   253  		}
   254  	}
   255  	scanner := bufio.NewScanner(bytes.NewBuffer(out))
   256  	for scanner.Scan() {
   257  		f := strings.Fields(scanner.Text())
   258  		var typ, name string
   259  		var csym bool
   260  		if iscgo {
   261  			if len(f) < 4 {
   262  				continue
   263  			}
   264  			csym = !strings.Contains(f[0], "_go_.o")
   265  			typ = f[2]
   266  			name = f[3]
   267  		} else {
   268  			if len(f) < 3 {
   269  				continue
   270  			}
   271  			typ = f[1]
   272  			name = f[2]
   273  		}
   274  		for i := range syms {
   275  			sym := &syms[i]
   276  			if sym.Type == typ && sym.Name == name && sym.CSym == csym {
   277  				if sym.Found {
   278  					t.Fatalf("duplicate symbol %s %s", sym.Type, sym.Name)
   279  				}
   280  				sym.Found = true
   281  			}
   282  		}
   283  	}
   284  	err = scanner.Err()
   285  	if err != nil {
   286  		t.Fatalf("error reading nm output: %v", err)
   287  	}
   288  	for _, sym := range syms {
   289  		if !sym.Found {
   290  			t.Errorf("cannot found symbol %s %s", sym.Type, sym.Name)
   291  		}
   292  	}
   293  }
   294  
   295  func TestGoLib(t *testing.T) {
   296  	testGoLib(t, false)
   297  }
   298  
   299  const testexec = `
   300  package main
   301  
   302  import "fmt"
   303  {{if .}}import "C"
   304  {{end}}
   305  
   306  func main() {
   307  	testfunc()
   308  }
   309  
   310  var testdata uint32
   311  
   312  func testfunc() {
   313  	fmt.Printf("main=%p\n", main)
   314  	fmt.Printf("testfunc=%p\n", testfunc)
   315  	fmt.Printf("testdata=%p\n", &testdata)
   316  }
   317  `
   318  
   319  const testlib = `
   320  package mylib
   321  
   322  {{if .}}
   323  // int cgodata = 5;
   324  // void cgofunc(void) {}
   325  import "C"
   326  
   327  var TestCgodata = C.cgodata
   328  
   329  func TestCgofunc() {
   330  	C.cgofunc()
   331  }
   332  {{end}}
   333  
   334  var Testdata uint32
   335  
   336  func Testfunc() {}
   337  `
   338  

View as plain text