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 }