Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1069)

Delta Between Two Patch Sets: src/cmd/objdump/main.go

Issue 91360046: code review 91360046: objdump: implement disassembly (Closed)
Left Patch Set: Created 9 years, 10 months ago
Right Patch Set: diff -r ab46827fd5f7 https://code.google.com/p/go/ Created 9 years, 10 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Right: Side by side diff | Download
« no previous file with change/comment | « src/cmd/objdump/macho.go ('k') | src/cmd/objdump/objdump_test.go » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
(no file at all)
1 // Copyright 2012 The Go Authors. All rights reserved. 1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 // Objdump is a minimal simulation of the GNU objdump tool, 5 // Objdump disassembles executable files.
6 // just enough to support pprof.
7 // 6 //
8 // Usage: 7 // Usage:
8 //
9 // go tool objdump [-s symregexp] binary
10 //
11 // Objdump prints a disassembly of all text symbols (code) in the binary.
12 // If the -s option is present, objdump only disassembles
13 // symbols with names matching the regular expression.
14 //
15 // Alternate usage:
16 //
9 // go tool objdump binary start end 17 // go tool objdump binary start end
10 // 18 //
11 // Objdump disassembles the binary starting at the start address and 19 // In this mode, objdump disassembles the binary starting at the start address a nd
12 // stopping at the end address. The start and end addresses are program 20 // stopping at the end address. The start and end addresses are program
13 // counters written in hexadecimal with optional leading 0x prefix. 21 // counters written in hexadecimal with optional leading 0x prefix.
14 // 22 // In this mode, objdump prints a sequence of stanzas of the form:
15 // It prints a sequence of stanzas of the form:
16 // 23 //
17 // file:line 24 // file:line
18 // address: assembly 25 // address: assembly
19 // address: assembly 26 // address: assembly
20 // ... 27 // ...
21 // 28 //
22 // Each stanza gives the disassembly for a contiguous range of addresses 29 // Each stanza gives the disassembly for a contiguous range of addresses
23 // all mapped to the same original source file and line number. 30 // all mapped to the same original source file and line number.
24 // 31 // This mode is intended for use by pprof.
25 // The disassembler is missing (golang.org/issue/7452) but will be added 32 //
33 // The ARM disassembler is missing (golang.org/issue/7452) but will be added
26 // before the Go 1.3 release. 34 // before the Go 1.3 release.
27 //
28 // This tool is intended for use only by pprof; its interface may change or
29 // it may be deleted entirely in future releases.
30 package main 35 package main
31 36
32 import ( 37 import (
33 "bufio" 38 "bufio"
39 "bytes"
34 "debug/elf" 40 "debug/elf"
35 "debug/gosym" 41 "debug/gosym"
36 "debug/macho" 42 "debug/macho"
37 "debug/pe" 43 "debug/pe"
38 "flag" 44 "flag"
39 "fmt" 45 "fmt"
46 "io"
40 "log" 47 "log"
41 "os" 48 "os"
49 "regexp"
50 "sort"
42 "strconv" 51 "strconv"
43 "strings" 52 "strings"
53 "text/tabwriter"
44 ) 54 )
45 55
46 func printUsage(w *os.File) { 56 var symregexp = flag.String("s", "", "only dump symbols matching this regexp")
47 » fmt.Fprintf(w, "usage: objdump binary start end\n") 57 var symRE *regexp.Regexp
48 » fmt.Fprintf(w, "disassembles binary from start PC to end PC.\n")
49 » fmt.Fprintf(w, "start and end are hexadecimal numbers with optional lead ing 0x prefix.\n")
50 }
51 58
52 func usage() { 59 func usage() {
53 » printUsage(os.Stderr) 60 » fmt.Fprintf(os.Stderr, "usage: go tool objdump [-s symregexp] binary [st art end]\n\n")
61 » flag.PrintDefaults()
54 os.Exit(2) 62 os.Exit(2)
55 } 63 }
64
65 type lookupFunc func(addr uint64) (sym string, base uint64)
66 type disasmFunc func(code []byte, pc uint64, lookup lookupFunc) (text string, si ze int)
56 67
57 func main() { 68 func main() {
58 log.SetFlags(0) 69 log.SetFlags(0)
59 log.SetPrefix("objdump: ") 70 log.SetPrefix("objdump: ")
60 71
61 flag.Usage = usage 72 flag.Usage = usage
62 flag.Parse() 73 flag.Parse()
63 » if flag.NArg() != 3 { 74 » if flag.NArg() != 1 && flag.NArg() != 3 {
64 usage() 75 usage()
65 } 76 }
66 77
78 if *symregexp != "" {
79 re, err := regexp.Compile(*symregexp)
80 if err != nil {
81 log.Fatalf("invalid -s regexp: %v", err)
82 }
83 symRE = re
84 }
85
67 f, err := os.Open(flag.Arg(0)) 86 f, err := os.Open(flag.Arg(0))
68 if err != nil { 87 if err != nil {
69 log.Fatal(err) 88 log.Fatal(err)
70 } 89 }
71 90
72 textStart, textData, symtab, pclntab, err := loadTables(f) 91 textStart, textData, symtab, pclntab, err := loadTables(f)
73 if err != nil { 92 if err != nil {
74 log.Fatalf("reading %s: %v", flag.Arg(0), err) 93 log.Fatalf("reading %s: %v", flag.Arg(0), err)
94 }
95
96 syms, goarch, err := loadSymbols(f)
97 if err != nil {
98 log.Fatalf("reading %s: %v", flag.Arg(0), err)
99 }
100
101 // Filter out section symbols, overwriting syms in place.
102 keep := syms[:0]
103 for _, sym := range syms {
104 switch sym.Name {
105 case "text", "_text", "etext", "_etext":
106 // drop
107 default:
108 keep = append(keep, sym)
109 }
110 }
111 syms = keep
112
113 disasm := disasms[goarch]
114 if disasm == nil {
115 log.Fatalf("reading %s: unknown architecture", flag.Arg(0))
116 }
117
118 lookup := func(addr uint64) (string, uint64) {
119 i := sort.Search(len(syms), func(i int) bool { return syms[i].Ad dr > addr })
120 if i > 0 {
121 s := syms[i-1]
122 if s.Addr <= addr && addr < s.Addr+uint64(s.Size) && s.N ame != "etext" && s.Name != "_etext" {
123 return s.Name, s.Addr
124 }
125 }
126 return "", 0
75 } 127 }
76 128
77 pcln := gosym.NewLineTable(pclntab, textStart) 129 pcln := gosym.NewLineTable(pclntab, textStart)
78 tab, err := gosym.NewTable(symtab, pcln) 130 tab, err := gosym.NewTable(symtab, pcln)
79 if err != nil { 131 if err != nil {
80 log.Fatalf("reading %s: %v", flag.Arg(0), err) 132 log.Fatalf("reading %s: %v", flag.Arg(0), err)
81 } 133 }
82 134
135 if flag.NArg() == 1 {
136 // disassembly of entire object - our format
137 dump(tab, lookup, disasm, syms, textData, textStart)
138 os.Exit(exitCode)
139 }
140
141 // disassembly of specific piece of object - gnu objdump format for ppro f
142 gnuDump(tab, lookup, disasm, textData, textStart)
143 os.Exit(exitCode)
144 }
145
146 // base returns the final element in the path.
147 // It works on both Windows and Unix paths.
148 func base(path string) string {
149 path = path[strings.LastIndex(path, "/")+1:]
150 path = path[strings.LastIndex(path, `\`)+1:]
151 return path
152 }
153
154 func dump(tab *gosym.Table, lookup lookupFunc, disasm disasmFunc, syms []Sym, te xtData []byte, textStart uint64) {
155 stdout := bufio.NewWriter(os.Stdout)
156 defer stdout.Flush()
157
158 printed := false
159 for _, sym := range syms {
160 if sym.Code != 'T' || sym.Size == 0 || sym.Name == "_text" || sy m.Name == "text" || sym.Addr < textStart || symRE != nil && !symRE.MatchString(s ym.Name) {
161 continue
162 }
163 if sym.Addr >= textStart+uint64(len(textData)) || sym.Addr+uint6 4(sym.Size) > textStart+uint64(len(textData)) {
164 break
165 }
166 if printed {
167 fmt.Fprintf(stdout, "\n")
168 } else {
169 printed = true
170 }
171 file, _, _ := tab.PCToLine(sym.Addr)
172 fmt.Fprintf(stdout, "TEXT %s(SB) %s\n", sym.Name, file)
173 tw := tabwriter.NewWriter(stdout, 1, 8, 1, '\t', 0)
174 start := sym.Addr
175 end := sym.Addr + uint64(sym.Size)
176 for pc := start; pc < end; {
177 i := pc - textStart
178 text, size := disasm(textData[i:end-textStart], pc, look up)
179 file, line, _ := tab.PCToLine(pc)
180 fmt.Fprintf(tw, "\t%s:%d\t%#x\t%x\t%s\n", base(file), li ne, pc, textData[i:i+uint64(size)], text)
181 pc += uint64(size)
182 }
183 tw.Flush()
184 }
185 }
186
187 func disasm_386(code []byte, pc uint64, lookup lookupFunc) (string, int) {
188 return disasm_x86(code, pc, lookup, 32)
189 }
190
191 func disasm_amd64(code []byte, pc uint64, lookup lookupFunc) (string, int) {
192 return disasm_x86(code, pc, lookup, 64)
193 }
194
195 func disasm_x86(code []byte, pc uint64, lookup lookupFunc, arch int) (string, in t) {
196 inst, err := x86_Decode(code, 64)
197 var text string
198 size := inst.Len
199 if err != nil || size == 0 || inst.Op == 0 {
200 size = 1
201 text = "?"
202 } else {
203 text = x86_plan9Syntax(inst, pc, lookup)
204 }
205 return text, size
206 }
207
208 func disasm_arm(code []byte, pc uint64, lookup lookupFunc) (string, int) {
209 /*
210 inst, size, err := arm_Decode(code, 64)
211 var text string
212 if err != nil || size == 0 || inst.Op == 0 {
213 size = 1
214 text = "?"
215 } else {
216 text = arm_plan9Syntax(inst, pc, lookup)
217 }
218 return text, size
219 */
220 return "?", 4
221 }
222
223 var disasms = map[string]disasmFunc{
224 "386": disasm_386,
225 "amd64": disasm_amd64,
226 "arm": disasm_arm,
227 }
228
229 func gnuDump(tab *gosym.Table, lookup lookupFunc, disasm disasmFunc, textData [] byte, textStart uint64) {
83 start, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(1), "0x"), 1 6, 64) 230 start, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(1), "0x"), 1 6, 64)
84 if err != nil { 231 if err != nil {
85 log.Fatalf("invalid start PC: %v", err) 232 log.Fatalf("invalid start PC: %v", err)
86 } 233 }
87 end, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(2), "0x"), 16, 64) 234 end, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(2), "0x"), 16, 64)
88 if err != nil { 235 if err != nil {
89 log.Fatalf("invalid end PC: %v", err) 236 log.Fatalf("invalid end PC: %v", err)
90 } 237 }
91 238
92 stdout := bufio.NewWriter(os.Stdout) 239 stdout := bufio.NewWriter(os.Stdout)
240 defer stdout.Flush()
93 241
94 // For now, find spans of same PC/line/fn and 242 // For now, find spans of same PC/line/fn and
95 // emit them as having dummy instructions. 243 // emit them as having dummy instructions.
96 var ( 244 var (
97 spanPC uint64 245 spanPC uint64
98 spanFile string 246 spanFile string
99 spanLine int 247 spanLine int
100 spanFn *gosym.Func 248 spanFn *gosym.Func
101 ) 249 )
102 250
103 flush := func(endPC uint64) { 251 flush := func(endPC uint64) {
104 if spanPC == 0 { 252 if spanPC == 0 {
105 return 253 return
106 } 254 }
107 fmt.Fprintf(stdout, "%s:%d\n", spanFile, spanLine) 255 fmt.Fprintf(stdout, "%s:%d\n", spanFile, spanLine)
108 » » for pc := spanPC; pc < endPC; pc++ { 256 » » for pc := spanPC; pc < endPC; {
109 » » » // TODO(rsc): Disassemble instructions here. 257 » » » text, size := disasm(textData[pc-textStart:], pc, lookup )
110 » » » if textStart <= pc && pc-textStart < uint64(len(textData )) { 258 » » » fmt.Fprintf(stdout, " %x: %s\n", pc, text)
111 » » » » fmt.Fprintf(stdout, " %x: byte %#x\n", pc, textD ata[pc-textStart]) 259 » » » pc += uint64(size)
112 » » » } else {
113 » » » » fmt.Fprintf(stdout, " %x: ?\n", pc)
114 » » » }
115 } 260 }
116 spanPC = 0 261 spanPC = 0
117 } 262 }
118 263
119 for pc := start; pc < end; pc++ { 264 for pc := start; pc < end; pc++ {
120 file, line, fn := tab.PCToLine(pc) 265 file, line, fn := tab.PCToLine(pc)
121 if file != spanFile || line != spanLine || fn != spanFn { 266 if file != spanFile || line != spanLine || fn != spanFn {
122 flush(pc) 267 flush(pc)
123 spanPC, spanFile, spanLine, spanFn = pc, file, line, fn 268 spanPC, spanFile, spanLine, spanFn = pc, file, line, fn
124 } 269 }
125 } 270 }
126 flush(end) 271 flush(end)
127
128 stdout.Flush()
129 } 272 }
130 273
131 func loadTables(f *os.File) (textStart uint64, textData, symtab, pclntab []byte, err error) { 274 func loadTables(f *os.File) (textStart uint64, textData, symtab, pclntab []byte, err error) {
132 if obj, err := elf.NewFile(f); err == nil { 275 if obj, err := elf.NewFile(f); err == nil {
133 if sect := obj.Section(".text"); sect != nil { 276 if sect := obj.Section(".text"); sect != nil {
134 textStart = sect.Addr 277 textStart = sect.Addr
135 textData, _ = sect.Data() 278 textData, _ = sect.Data()
136 } 279 }
137 if sect := obj.Section(".gosymtab"); sect != nil { 280 if sect := obj.Section(".gosymtab"); sect != nil {
138 if symtab, err = sect.Data(); err != nil { 281 if symtab, err = sect.Data(); err != nil {
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
210 if ssym.SectionNumber != esym.SectionNumber { 353 if ssym.SectionNumber != esym.SectionNumber {
211 return nil, fmt.Errorf("%s and %s symbols must be in the same se ction", sname, ename) 354 return nil, fmt.Errorf("%s and %s symbols must be in the same se ction", sname, ename)
212 } 355 }
213 sect := f.Sections[ssym.SectionNumber-1] 356 sect := f.Sections[ssym.SectionNumber-1]
214 data, err := sect.Data() 357 data, err := sect.Data()
215 if err != nil { 358 if err != nil {
216 return nil, err 359 return nil, err
217 } 360 }
218 return data[ssym.Value:esym.Value], nil 361 return data[ssym.Value:esym.Value], nil
219 } 362 }
363
364 // TODO(rsc): This code is taken from cmd/nm. Arrange some way to share the code .
365
366 var exitCode = 0
367
368 func errorf(format string, args ...interface{}) {
369 log.Printf(format, args...)
370 exitCode = 1
371 }
372
373 func loadSymbols(f *os.File) (syms []Sym, goarch string, err error) {
374 f.Seek(0, 0)
375 buf := make([]byte, 16)
376 io.ReadFull(f, buf)
377 f.Seek(0, 0)
378
379 for _, p := range parsers {
380 if bytes.HasPrefix(buf, p.prefix) {
381 syms, goarch = p.parse(f)
382 sort.Sort(byAddr(syms))
383 return
384 }
385 }
386 err = fmt.Errorf("unknown file format")
387 return
388 }
389
390 type Sym struct {
391 Addr uint64
392 Size int64
393 Code rune
394 Name string
395 Type string
396 }
397
398 var parsers = []struct {
399 prefix []byte
400 parse func(*os.File) ([]Sym, string)
401 }{
402 {[]byte("\x7FELF"), elfSymbols},
403 {[]byte("\xFE\xED\xFA\xCE"), machoSymbols},
404 {[]byte("\xFE\xED\xFA\xCF"), machoSymbols},
405 {[]byte("\xCE\xFA\xED\xFE"), machoSymbols},
406 {[]byte("\xCF\xFA\xED\xFE"), machoSymbols},
407 {[]byte("MZ"), peSymbols},
408 {[]byte("\x00\x00\x01\xEB"), plan9Symbols}, // 386
409 {[]byte("\x00\x00\x04\x07"), plan9Symbols}, // mips
410 {[]byte("\x00\x00\x06\x47"), plan9Symbols}, // arm
411 {[]byte("\x00\x00\x8A\x97"), plan9Symbols}, // amd64
412 }
413
414 type byAddr []Sym
415
416 func (x byAddr) Len() int { return len(x) }
417 func (x byAddr) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
418 func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b