summaryrefslogtreecommitdiff
path: root/src/cmd/objdump/objdump_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/objdump/objdump_test.go')
-rw-r--r--src/cmd/objdump/objdump_test.go193
1 files changed, 193 insertions, 0 deletions
diff --git a/src/cmd/objdump/objdump_test.go b/src/cmd/objdump/objdump_test.go
new file mode 100644
index 000000000..82311bb1f
--- /dev/null
+++ b/src/cmd/objdump/objdump_test.go
@@ -0,0 +1,193 @@
+// 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 (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func loadSyms(t *testing.T) map[string]string {
+ if runtime.GOOS == "nacl" {
+ t.Skip("skipping on nacl")
+ }
+
+ cmd := exec.Command("go", "tool", "nm", os.Args[0])
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("go tool nm %v: %v\n%s", os.Args[0], err, string(out))
+ }
+ syms := make(map[string]string)
+ scanner := bufio.NewScanner(bytes.NewReader(out))
+ for scanner.Scan() {
+ f := strings.Fields(scanner.Text())
+ if len(f) < 3 {
+ continue
+ }
+ syms[f[2]] = f[0]
+ }
+ if err := scanner.Err(); err != nil {
+ t.Fatalf("error reading symbols: %v", err)
+ }
+ return syms
+}
+
+func runObjDump(t *testing.T, exe, startaddr, endaddr string) (path, lineno string) {
+ if runtime.GOOS == "nacl" {
+ t.Skip("skipping on nacl")
+ }
+
+ cmd := exec.Command(exe, os.Args[0], startaddr, endaddr)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("go tool objdump %v: %v\n%s", os.Args[0], err, string(out))
+ }
+ f := strings.Split(string(out), "\n")
+ if len(f) < 1 {
+ t.Fatal("objdump output must have at least one line")
+ }
+ pathAndLineNo := f[0]
+ f = strings.Split(pathAndLineNo, ":")
+ if runtime.GOOS == "windows" {
+ switch len(f) {
+ case 2:
+ return f[0], f[1]
+ case 3:
+ return f[0] + ":" + f[1], f[2]
+ default:
+ t.Fatalf("no line number found in %q", pathAndLineNo)
+ }
+ }
+ if len(f) != 2 {
+ t.Fatalf("no line number found in %q", pathAndLineNo)
+ }
+ return f[0], f[1]
+}
+
+func testObjDump(t *testing.T, exe, startaddr, endaddr string, line int) {
+ srcPath, srcLineNo := runObjDump(t, exe, startaddr, endaddr)
+ fi1, err := os.Stat("objdump_test.go")
+ if err != nil {
+ t.Fatalf("Stat failed: %v", err)
+ }
+ fi2, err := os.Stat(srcPath)
+ if err != nil {
+ t.Fatalf("Stat failed: %v", err)
+ }
+ if !os.SameFile(fi1, fi2) {
+ t.Fatalf("objdump_test.go and %s are not same file", srcPath)
+ }
+ if srcLineNo != fmt.Sprint(line) {
+ t.Fatalf("line number = %v; want %d", srcLineNo, line)
+ }
+}
+
+func TestObjDump(t *testing.T) {
+ _, _, line, _ := runtime.Caller(0)
+ syms := loadSyms(t)
+
+ tmp, exe := buildObjdump(t)
+ defer os.RemoveAll(tmp)
+
+ startaddr := syms["cmd/objdump.TestObjDump"]
+ addr, err := strconv.ParseUint(startaddr, 16, 64)
+ if err != nil {
+ t.Fatalf("invalid start address %v: %v", startaddr, err)
+ }
+ endaddr := fmt.Sprintf("%x", addr+10)
+ testObjDump(t, exe, startaddr, endaddr, line-1)
+ testObjDump(t, exe, "0x"+startaddr, "0x"+endaddr, line-1)
+}
+
+func buildObjdump(t *testing.T) (tmp, exe string) {
+ if runtime.GOOS == "nacl" {
+ t.Skip("skipping on nacl")
+ }
+
+ tmp, err := ioutil.TempDir("", "TestObjDump")
+ if err != nil {
+ t.Fatal("TempDir failed: ", err)
+ }
+
+ exe = filepath.Join(tmp, "testobjdump.exe")
+ out, err := exec.Command("go", "build", "-o", exe, "cmd/objdump").CombinedOutput()
+ if err != nil {
+ os.RemoveAll(tmp)
+ t.Fatalf("go build -o %v cmd/objdump: %v\n%s", exe, err, string(out))
+ }
+ return
+}
+
+var x86Need = []string{
+ "fmthello.go:6",
+ "TEXT main.main(SB)",
+ "JMP main.main(SB)",
+ "CALL fmt.Println(SB)",
+ "RET",
+}
+
+var armNeed = []string{
+ "fmthello.go:6",
+ "TEXT main.main(SB)",
+ "B.LS main.main(SB)",
+ "BL fmt.Println(SB)",
+ "RET",
+}
+
+// objdump is fully cross platform: it can handle binaries
+// from any known operating system and architecture.
+// We could in principle add binaries to testdata and check
+// all the supported systems during this test. However, the
+// binaries would be about 1 MB each, and we don't want to
+// add that much junk to the hg repository. Instead, build a
+// binary for the current system (only) and test that objdump
+// can handle that one.
+
+func TestDisasm(t *testing.T) {
+ tmp, exe := buildObjdump(t)
+ defer os.RemoveAll(tmp)
+
+ hello := filepath.Join(tmp, "hello.exe")
+ out, err := exec.Command("go", "build", "-o", hello, "testdata/fmthello.go").CombinedOutput()
+ if err != nil {
+ t.Fatalf("go build fmthello.go: %v\n%s", err, out)
+ }
+ need := []string{
+ "fmthello.go:6",
+ "TEXT main.main(SB)",
+ }
+ switch runtime.GOARCH {
+ case "amd64", "386":
+ need = append(need, x86Need...)
+ case "arm":
+ need = append(need, armNeed...)
+ }
+
+ out, err = exec.Command(exe, "-s", "main.main", hello).CombinedOutput()
+ if err != nil {
+ t.Fatalf("objdump fmthello.exe: %v\n%s", err, out)
+ }
+
+ text := string(out)
+ ok := true
+ for _, s := range need {
+ if !strings.Contains(text, s) {
+ t.Errorf("disassembly missing '%s'", s)
+ ok = false
+ }
+ }
+ if !ok {
+ t.Logf("full disassembly:\n%s", text)
+ }
+}