github.com/razvanm/vanadium-go-1.3@v0.0.0-20160721203343-4a65068e5915/src/cmd/objdump/main.go (about) 1 // Copyright 2012 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 // Objdump disassembles executable files. 6 // 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 // 17 // go tool objdump binary start end 18 // 19 // In this mode, objdump disassembles the binary starting at the start address and 20 // stopping at the end address. The start and end addresses are program 21 // counters written in hexadecimal with optional leading 0x prefix. 22 // In this mode, objdump prints a sequence of stanzas of the form: 23 // 24 // file:line 25 // address: assembly 26 // address: assembly 27 // ... 28 // 29 // Each stanza gives the disassembly for a contiguous range of addresses 30 // all mapped to the same original source file and line number. 31 // This mode is intended for use by pprof. 32 package main 33 34 import ( 35 "bufio" 36 "bytes" 37 "debug/elf" 38 "debug/gosym" 39 "debug/macho" 40 "debug/pe" 41 "debug/plan9obj" 42 "encoding/binary" 43 "flag" 44 "fmt" 45 "io" 46 "log" 47 "os" 48 "regexp" 49 "sort" 50 "strconv" 51 "strings" 52 "text/tabwriter" 53 54 "cmd/internal/rsc.io/arm/armasm" 55 "cmd/internal/rsc.io/x86/x86asm" 56 ) 57 58 var symregexp = flag.String("s", "", "only dump symbols matching this regexp") 59 var symRE *regexp.Regexp 60 61 func usage() { 62 fmt.Fprintf(os.Stderr, "usage: go tool objdump [-s symregexp] binary [start end]\n\n") 63 flag.PrintDefaults() 64 os.Exit(2) 65 } 66 67 type lookupFunc func(addr uint64) (sym string, base uint64) 68 type disasmFunc func(code []byte, pc uint64, lookup lookupFunc) (text string, size int) 69 70 func main() { 71 log.SetFlags(0) 72 log.SetPrefix("objdump: ") 73 74 flag.Usage = usage 75 flag.Parse() 76 if flag.NArg() != 1 && flag.NArg() != 3 { 77 usage() 78 } 79 80 if *symregexp != "" { 81 re, err := regexp.Compile(*symregexp) 82 if err != nil { 83 log.Fatalf("invalid -s regexp: %v", err) 84 } 85 symRE = re 86 } 87 88 f, err := os.Open(flag.Arg(0)) 89 if err != nil { 90 log.Fatal(err) 91 } 92 93 textStart, textData, symtab, pclntab, err := loadTables(f) 94 if err != nil { 95 log.Fatalf("reading %s: %v", flag.Arg(0), err) 96 } 97 98 syms, goarch, err := loadSymbols(f) 99 if err != nil { 100 log.Fatalf("reading %s: %v", flag.Arg(0), err) 101 } 102 103 // Filter out section symbols, overwriting syms in place. 104 keep := syms[:0] 105 for _, sym := range syms { 106 switch sym.Name { 107 case "runtime.text", "text", "_text", "runtime.etext", "etext", "_etext": 108 // drop 109 default: 110 keep = append(keep, sym) 111 } 112 } 113 syms = keep 114 115 disasm := disasms[goarch] 116 if disasm == nil { 117 log.Fatalf("reading %s: unknown architecture", flag.Arg(0)) 118 } 119 120 lookup := func(addr uint64) (string, uint64) { 121 i := sort.Search(len(syms), func(i int) bool { return syms[i].Addr > addr }) 122 if i > 0 { 123 s := syms[i-1] 124 if s.Addr <= addr && addr < s.Addr+uint64(s.Size) && s.Name != "runtime.etext" && s.Name != "etext" && s.Name != "_etext" { 125 return s.Name, s.Addr 126 } 127 } 128 return "", 0 129 } 130 131 pcln := gosym.NewLineTable(pclntab, textStart) 132 tab, err := gosym.NewTable(symtab, pcln) 133 if err != nil { 134 log.Fatalf("reading %s: %v", flag.Arg(0), err) 135 } 136 137 if flag.NArg() == 1 { 138 // disassembly of entire object - our format 139 dump(tab, lookup, disasm, goarch, syms, textData, textStart) 140 os.Exit(exitCode) 141 } 142 143 // disassembly of specific piece of object - gnu objdump format for pprof 144 gnuDump(tab, lookup, disasm, textData, textStart) 145 os.Exit(exitCode) 146 } 147 148 // base returns the final element in the path. 149 // It works on both Windows and Unix paths. 150 func base(path string) string { 151 path = path[strings.LastIndex(path, "/")+1:] 152 path = path[strings.LastIndex(path, `\`)+1:] 153 return path 154 } 155 156 func dump(tab *gosym.Table, lookup lookupFunc, disasm disasmFunc, goarch string, syms []Sym, textData []byte, textStart uint64) { 157 stdout := bufio.NewWriter(os.Stdout) 158 defer stdout.Flush() 159 160 printed := false 161 for _, sym := range syms { 162 if sym.Code != 'T' || sym.Size == 0 || sym.Name == "_text" || sym.Name == "text" || sym.Addr < textStart || symRE != nil && !symRE.MatchString(sym.Name) { 163 continue 164 } 165 if sym.Addr >= textStart+uint64(len(textData)) || sym.Addr+uint64(sym.Size) > textStart+uint64(len(textData)) { 166 break 167 } 168 if printed { 169 fmt.Fprintf(stdout, "\n") 170 } else { 171 printed = true 172 } 173 file, _, _ := tab.PCToLine(sym.Addr) 174 fmt.Fprintf(stdout, "TEXT %s(SB) %s\n", sym.Name, file) 175 tw := tabwriter.NewWriter(stdout, 1, 8, 1, '\t', 0) 176 start := sym.Addr 177 end := sym.Addr + uint64(sym.Size) 178 for pc := start; pc < end; { 179 i := pc - textStart 180 text, size := disasm(textData[i:end-textStart], pc, lookup) 181 file, line, _ := tab.PCToLine(pc) 182 183 // ARM is word-based, so show actual word hex, not byte hex. 184 // Since ARM is little endian, they're different. 185 if goarch == "arm" && size == 4 { 186 fmt.Fprintf(tw, "\t%s:%d\t%#x\t%08x\t%s\n", base(file), line, pc, binary.LittleEndian.Uint32(textData[i:i+uint64(size)]), text) 187 } else { 188 fmt.Fprintf(tw, "\t%s:%d\t%#x\t%x\t%s\n", base(file), line, pc, textData[i:i+uint64(size)], text) 189 } 190 pc += uint64(size) 191 } 192 tw.Flush() 193 } 194 } 195 196 func disasm_386(code []byte, pc uint64, lookup lookupFunc) (string, int) { 197 return disasm_x86(code, pc, lookup, 32) 198 } 199 200 func disasm_amd64(code []byte, pc uint64, lookup lookupFunc) (string, int) { 201 return disasm_x86(code, pc, lookup, 64) 202 } 203 204 func disasm_x86(code []byte, pc uint64, lookup lookupFunc, arch int) (string, int) { 205 inst, err := x86asm.Decode(code, 64) 206 var text string 207 size := inst.Len 208 if err != nil || size == 0 || inst.Op == 0 { 209 size = 1 210 text = "?" 211 } else { 212 text = x86asm.Plan9Syntax(inst, pc, lookup) 213 } 214 return text, size 215 } 216 217 type textReader struct { 218 code []byte 219 pc uint64 220 } 221 222 func (r textReader) ReadAt(data []byte, off int64) (n int, err error) { 223 if off < 0 || uint64(off) < r.pc { 224 return 0, io.EOF 225 } 226 d := uint64(off) - r.pc 227 if d >= uint64(len(r.code)) { 228 return 0, io.EOF 229 } 230 n = copy(data, r.code[d:]) 231 if n < len(data) { 232 err = io.ErrUnexpectedEOF 233 } 234 return 235 } 236 237 func disasm_arm(code []byte, pc uint64, lookup lookupFunc) (string, int) { 238 inst, err := armasm.Decode(code, armasm.ModeARM) 239 var text string 240 size := inst.Len 241 if err != nil || size == 0 || inst.Op == 0 { 242 size = 4 243 text = "?" 244 } else { 245 text = armasm.Plan9Syntax(inst, pc, lookup, textReader{code, pc}) 246 } 247 return text, size 248 } 249 250 var disasms = map[string]disasmFunc{ 251 "386": disasm_386, 252 "amd64": disasm_amd64, 253 "arm": disasm_arm, 254 } 255 256 func gnuDump(tab *gosym.Table, lookup lookupFunc, disasm disasmFunc, textData []byte, textStart uint64) { 257 start, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(1), "0x"), 16, 64) 258 if err != nil { 259 log.Fatalf("invalid start PC: %v", err) 260 } 261 end, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(2), "0x"), 16, 64) 262 if err != nil { 263 log.Fatalf("invalid end PC: %v", err) 264 } 265 if start < textStart { 266 start = textStart 267 } 268 if end < start { 269 end = start 270 } 271 if end > textStart+uint64(len(textData)) { 272 end = textStart + uint64(len(textData)) 273 } 274 275 stdout := bufio.NewWriter(os.Stdout) 276 defer stdout.Flush() 277 278 // For now, find spans of same PC/line/fn and 279 // emit them as having dummy instructions. 280 var ( 281 spanPC uint64 282 spanFile string 283 spanLine int 284 spanFn *gosym.Func 285 ) 286 287 flush := func(endPC uint64) { 288 if spanPC == 0 { 289 return 290 } 291 fmt.Fprintf(stdout, "%s:%d\n", spanFile, spanLine) 292 for pc := spanPC; pc < endPC; { 293 text, size := disasm(textData[pc-textStart:], pc, lookup) 294 fmt.Fprintf(stdout, " %x: %s\n", pc, text) 295 pc += uint64(size) 296 } 297 spanPC = 0 298 } 299 300 for pc := start; pc < end; pc++ { 301 file, line, fn := tab.PCToLine(pc) 302 if file != spanFile || line != spanLine || fn != spanFn { 303 flush(pc) 304 spanPC, spanFile, spanLine, spanFn = pc, file, line, fn 305 } 306 } 307 flush(end) 308 } 309 310 func loadTables(f *os.File) (textStart uint64, textData, symtab, pclntab []byte, err error) { 311 if obj, err := elf.NewFile(f); err == nil { 312 if sect := obj.Section(".text"); sect != nil { 313 textStart = sect.Addr 314 textData, _ = sect.Data() 315 } 316 if sect := obj.Section(".gosymtab"); sect != nil { 317 if symtab, err = sect.Data(); err != nil { 318 return 0, nil, nil, nil, err 319 } 320 } 321 if sect := obj.Section(".gopclntab"); sect != nil { 322 if pclntab, err = sect.Data(); err != nil { 323 return 0, nil, nil, nil, err 324 } 325 } 326 return textStart, textData, symtab, pclntab, nil 327 } 328 329 if obj, err := macho.NewFile(f); err == nil { 330 if sect := obj.Section("__text"); sect != nil { 331 textStart = sect.Addr 332 textData, _ = sect.Data() 333 } 334 if sect := obj.Section("__gosymtab"); sect != nil { 335 if symtab, err = sect.Data(); err != nil { 336 return 0, nil, nil, nil, err 337 } 338 } 339 if sect := obj.Section("__gopclntab"); sect != nil { 340 if pclntab, err = sect.Data(); err != nil { 341 return 0, nil, nil, nil, err 342 } 343 } 344 return textStart, textData, symtab, pclntab, nil 345 } 346 347 if obj, err := pe.NewFile(f); err == nil { 348 var imageBase uint64 349 switch oh := obj.OptionalHeader.(type) { 350 case *pe.OptionalHeader32: 351 imageBase = uint64(oh.ImageBase) 352 case *pe.OptionalHeader64: 353 imageBase = oh.ImageBase 354 default: 355 return 0, nil, nil, nil, fmt.Errorf("pe file format not recognized") 356 } 357 if sect := obj.Section(".text"); sect != nil { 358 textStart = imageBase + uint64(sect.VirtualAddress) 359 textData, _ = sect.Data() 360 } 361 if pclntab, err = loadPETable(obj, "runtime.pclntab", "runtime.epclntab"); err != nil { 362 // We didn't find the symbols, so look for the names used in 1.3 and earlier. 363 // TODO: Remove code looking for the old symbols when we no longer care about 1.3. 364 var err2 error 365 if pclntab, err2 = loadPETable(obj, "pclntab", "epclntab"); err2 != nil { 366 return 0, nil, nil, nil, err 367 } 368 } 369 if symtab, err = loadPETable(obj, "runtime.symtab", "runtime.esymtab"); err != nil { 370 // Same as above. 371 var err2 error 372 if symtab, err2 = loadPETable(obj, "symtab", "esymtab"); err2 != nil { 373 return 0, nil, nil, nil, err 374 } 375 } 376 return textStart, textData, symtab, pclntab, nil 377 } 378 379 if obj, err := plan9obj.NewFile(f); err == nil { 380 textStart = obj.LoadAddress + obj.HdrSize 381 if sect := obj.Section("text"); sect != nil { 382 textData, _ = sect.Data() 383 } 384 if pclntab, err = loadPlan9Table(obj, "runtime.pclntab", "runtime.epclntab"); err != nil { 385 // We didn't find the symbols, so look for the names used in 1.3 and earlier. 386 // TODO: Remove code looking for the old symbols when we no longer care about 1.3. 387 var err2 error 388 if pclntab, err2 = loadPlan9Table(obj, "pclntab", "epclntab"); err2 != nil { 389 return 0, nil, nil, nil, err 390 } 391 } 392 if symtab, err = loadPlan9Table(obj, "runtime.symtab", "runtime.esymtab"); err != nil { 393 // Same as above. 394 var err2 error 395 if symtab, err2 = loadPlan9Table(obj, "symtab", "esymtab"); err2 != nil { 396 return 0, nil, nil, nil, err 397 } 398 } 399 return textStart, textData, symtab, pclntab, nil 400 } 401 402 return 0, nil, nil, nil, fmt.Errorf("unrecognized binary format") 403 } 404 405 func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) { 406 for _, s := range f.Symbols { 407 if s.Name != name { 408 continue 409 } 410 if s.SectionNumber <= 0 { 411 return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber) 412 } 413 if len(f.Sections) < int(s.SectionNumber) { 414 return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections)) 415 } 416 return s, nil 417 } 418 return nil, fmt.Errorf("no %s symbol found", name) 419 } 420 421 func loadPETable(f *pe.File, sname, ename string) ([]byte, error) { 422 ssym, err := findPESymbol(f, sname) 423 if err != nil { 424 return nil, err 425 } 426 esym, err := findPESymbol(f, ename) 427 if err != nil { 428 return nil, err 429 } 430 if ssym.SectionNumber != esym.SectionNumber { 431 return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename) 432 } 433 sect := f.Sections[ssym.SectionNumber-1] 434 data, err := sect.Data() 435 if err != nil { 436 return nil, err 437 } 438 return data[ssym.Value:esym.Value], nil 439 } 440 441 func findPlan9Symbol(f *plan9obj.File, name string) (*plan9obj.Sym, error) { 442 syms, err := f.Symbols() 443 if err != nil { 444 return nil, err 445 } 446 for _, s := range syms { 447 if s.Name != name { 448 continue 449 } 450 return &s, nil 451 } 452 return nil, fmt.Errorf("no %s symbol found", name) 453 } 454 455 func loadPlan9Table(f *plan9obj.File, sname, ename string) ([]byte, error) { 456 ssym, err := findPlan9Symbol(f, sname) 457 if err != nil { 458 return nil, err 459 } 460 esym, err := findPlan9Symbol(f, ename) 461 if err != nil { 462 return nil, err 463 } 464 sect := f.Section("text") 465 if sect == nil { 466 return nil, err 467 } 468 data, err := sect.Data() 469 if err != nil { 470 return nil, err 471 } 472 textStart := f.LoadAddress + f.HdrSize 473 return data[ssym.Value-textStart : esym.Value-textStart], nil 474 } 475 476 // TODO(rsc): This code is taken from cmd/nm. Arrange some way to share the code. 477 478 var exitCode = 0 479 480 func errorf(format string, args ...interface{}) { 481 log.Printf(format, args...) 482 exitCode = 1 483 } 484 485 func loadSymbols(f *os.File) (syms []Sym, goarch string, err error) { 486 f.Seek(0, 0) 487 buf := make([]byte, 16) 488 io.ReadFull(f, buf) 489 f.Seek(0, 0) 490 491 for _, p := range parsers { 492 if bytes.HasPrefix(buf, p.prefix) { 493 syms, goarch = p.parse(f) 494 sort.Sort(byAddr(syms)) 495 return 496 } 497 } 498 err = fmt.Errorf("unknown file format") 499 return 500 } 501 502 type Sym struct { 503 Addr uint64 504 Size int64 505 Code rune 506 Name string 507 Type string 508 } 509 510 var parsers = []struct { 511 prefix []byte 512 parse func(*os.File) ([]Sym, string) 513 }{ 514 {[]byte("\x7FELF"), elfSymbols}, 515 {[]byte("\xFE\xED\xFA\xCE"), machoSymbols}, 516 {[]byte("\xFE\xED\xFA\xCF"), machoSymbols}, 517 {[]byte("\xCE\xFA\xED\xFE"), machoSymbols}, 518 {[]byte("\xCF\xFA\xED\xFE"), machoSymbols}, 519 {[]byte("MZ"), peSymbols}, 520 {[]byte("\x00\x00\x01\xEB"), plan9Symbols}, // 386 521 {[]byte("\x00\x00\x04\x07"), plan9Symbols}, // mips 522 {[]byte("\x00\x00\x06\x47"), plan9Symbols}, // arm 523 {[]byte("\x00\x00\x8A\x97"), plan9Symbols}, // amd64 524 } 525 526 type byAddr []Sym 527 528 func (x byAddr) Len() int { return len(x) } 529 func (x byAddr) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 530 func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr }