github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/src/cmd/objdump/objdump_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  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"runtime"
    16  	"strconv"
    17  	"strings"
    18  	"testing"
    19  )
    20  
    21  func loadSyms(t *testing.T) map[string]string {
    22  	if runtime.GOOS == "nacl" {
    23  		t.Skip("skipping on nacl")
    24  	}
    25  
    26  	cmd := exec.Command("go", "tool", "nm", os.Args[0])
    27  	out, err := cmd.CombinedOutput()
    28  	if err != nil {
    29  		t.Fatalf("go tool nm %v: %v\n%s", os.Args[0], err, string(out))
    30  	}
    31  	syms := make(map[string]string)
    32  	scanner := bufio.NewScanner(bytes.NewReader(out))
    33  	for scanner.Scan() {
    34  		f := strings.Fields(scanner.Text())
    35  		if len(f) < 3 {
    36  			continue
    37  		}
    38  		syms[f[2]] = f[0]
    39  	}
    40  	if err := scanner.Err(); err != nil {
    41  		t.Fatalf("error reading symbols: %v", err)
    42  	}
    43  	return syms
    44  }
    45  
    46  func runObjDump(t *testing.T, exe, startaddr, endaddr string) (path, lineno string) {
    47  	if runtime.GOOS == "nacl" {
    48  		t.Skip("skipping on nacl")
    49  	}
    50  
    51  	cmd := exec.Command(exe, os.Args[0], startaddr, endaddr)
    52  	out, err := cmd.CombinedOutput()
    53  	if err != nil {
    54  		t.Fatalf("go tool objdump %v: %v\n%s", os.Args[0], err, string(out))
    55  	}
    56  	f := strings.Split(string(out), "\n")
    57  	if len(f) < 1 {
    58  		t.Fatal("objdump output must have at least one line")
    59  	}
    60  	pathAndLineNo := f[0]
    61  	f = strings.Split(pathAndLineNo, ":")
    62  	if runtime.GOOS == "windows" {
    63  		switch len(f) {
    64  		case 2:
    65  			return f[0], f[1]
    66  		case 3:
    67  			return f[0] + ":" + f[1], f[2]
    68  		default:
    69  			t.Fatalf("no line number found in %q", pathAndLineNo)
    70  		}
    71  	}
    72  	if len(f) != 2 {
    73  		t.Fatalf("no line number found in %q", pathAndLineNo)
    74  	}
    75  	return f[0], f[1]
    76  }
    77  
    78  func testObjDump(t *testing.T, exe, startaddr, endaddr string, line int) {
    79  	srcPath, srcLineNo := runObjDump(t, exe, startaddr, endaddr)
    80  	fi1, err := os.Stat("objdump_test.go")
    81  	if err != nil {
    82  		t.Fatalf("Stat failed: %v", err)
    83  	}
    84  	fi2, err := os.Stat(srcPath)
    85  	if err != nil {
    86  		t.Fatalf("Stat failed: %v", err)
    87  	}
    88  	if !os.SameFile(fi1, fi2) {
    89  		t.Fatalf("objdump_test.go and %s are not same file", srcPath)
    90  	}
    91  	if srcLineNo != fmt.Sprint(line) {
    92  		t.Fatalf("line number = %v; want %d", srcLineNo, line)
    93  	}
    94  }
    95  
    96  func TestObjDump(t *testing.T) {
    97  	_, _, line, _ := runtime.Caller(0)
    98  	syms := loadSyms(t)
    99  
   100  	tmp, exe := buildObjdump(t)
   101  	defer os.RemoveAll(tmp)
   102  
   103  	startaddr := syms["cmd/objdump.TestObjDump"]
   104  	addr, err := strconv.ParseUint(startaddr, 16, 64)
   105  	if err != nil {
   106  		t.Fatalf("invalid start address %v: %v", startaddr, err)
   107  	}
   108  	endaddr := fmt.Sprintf("%x", addr+10)
   109  	testObjDump(t, exe, startaddr, endaddr, line-1)
   110  	testObjDump(t, exe, "0x"+startaddr, "0x"+endaddr, line-1)
   111  }
   112  
   113  func buildObjdump(t *testing.T) (tmp, exe string) {
   114  	if runtime.GOOS == "nacl" {
   115  		t.Skip("skipping on nacl")
   116  	}
   117  
   118  	tmp, err := ioutil.TempDir("", "TestObjDump")
   119  	if err != nil {
   120  		t.Fatal("TempDir failed: ", err)
   121  	}
   122  
   123  	exe = filepath.Join(tmp, "testobjdump.exe")
   124  	out, err := exec.Command("go", "build", "-o", exe, "cmd/objdump").CombinedOutput()
   125  	if err != nil {
   126  		os.RemoveAll(tmp)
   127  		t.Fatalf("go build -o %v cmd/objdump: %v\n%s", exe, err, string(out))
   128  	}
   129  	return
   130  }
   131  
   132  var x86Need = []string{
   133  	"fmthello.go:6",
   134  	"TEXT main.main(SB)",
   135  	"JMP main.main(SB)",
   136  	"CALL fmt.Println(SB)",
   137  	"RET",
   138  }
   139  
   140  var armNeed = []string{
   141  	"fmthello.go:6",
   142  	"TEXT main.main(SB)",
   143  	"B.LS main.main(SB)",
   144  	"BL fmt.Println(SB)",
   145  	"RET",
   146  }
   147  
   148  // objdump is fully cross platform: it can handle binaries
   149  // from any known operating system and architecture.
   150  // We could in principle add binaries to testdata and check
   151  // all the supported systems during this test. However, the
   152  // binaries would be about 1 MB each, and we don't want to
   153  // add that much junk to the hg repository. Instead, build a
   154  // binary for the current system (only) and test that objdump
   155  // can handle that one.
   156  
   157  func TestDisasm(t *testing.T) {
   158  	tmp, exe := buildObjdump(t)
   159  	defer os.RemoveAll(tmp)
   160  
   161  	hello := filepath.Join(tmp, "hello.exe")
   162  	out, err := exec.Command("go", "build", "-o", hello, "testdata/fmthello.go").CombinedOutput()
   163  	if err != nil {
   164  		t.Fatalf("go build fmthello.go: %v\n%s", err, out)
   165  	}
   166  	need := []string{
   167  		"fmthello.go:6",
   168  		"TEXT main.main(SB)",
   169  	}
   170  	switch runtime.GOARCH {
   171  	case "amd64", "386":
   172  		need = append(need, x86Need...)
   173  	case "arm":
   174  		need = append(need, armNeed...)
   175  	}
   176  
   177  	out, err = exec.Command(exe, "-s", "main.main", hello).CombinedOutput()
   178  	if err != nil {
   179  		t.Fatalf("objdump fmthello.exe: %v\n%s", err, out)
   180  	}
   181  
   182  	text := string(out)
   183  	ok := true
   184  	for _, s := range need {
   185  		if !strings.Contains(text, s) {
   186  			t.Errorf("disassembly missing '%s'", s)
   187  			ok = false
   188  		}
   189  	}
   190  	if !ok {
   191  		t.Logf("full disassembly:\n%s", text)
   192  	}
   193  }