2014-04-14 10:58:49 -04:00
|
|
|
// Copyright 2012 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.
|
|
|
|
|
|
2014-04-15 20:06:08 -04:00
|
|
|
// Objdump is a minimal simulation of the GNU objdump tool,
|
|
|
|
|
// just enough to support pprof.
|
|
|
|
|
//
|
|
|
|
|
// Usage:
|
|
|
|
|
// go tool objdump binary start end
|
|
|
|
|
//
|
|
|
|
|
// Objdump disassembles the binary starting at the start address and
|
|
|
|
|
// stopping at the end address. The start and end addresses are program
|
2014-05-08 01:25:56 -04:00
|
|
|
// counters written in hexadecimal with optional leading 0x prefix.
|
2014-04-15 20:06:08 -04:00
|
|
|
//
|
|
|
|
|
// It prints a sequence of stanzas of the form:
|
|
|
|
|
//
|
|
|
|
|
// file:line
|
|
|
|
|
// address: assembly
|
|
|
|
|
// address: assembly
|
|
|
|
|
// ...
|
|
|
|
|
//
|
|
|
|
|
// Each stanza gives the disassembly for a contiguous range of addresses
|
|
|
|
|
// all mapped to the same original source file and line number.
|
|
|
|
|
//
|
|
|
|
|
// The disassembler is missing (golang.org/issue/7452) but will be added
|
|
|
|
|
// before the Go 1.3 release.
|
|
|
|
|
//
|
|
|
|
|
// This tool is intended for use only by pprof; its interface may change or
|
|
|
|
|
// it may be deleted entirely in future releases.
|
2014-04-14 10:58:49 -04:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"debug/elf"
|
|
|
|
|
"debug/gosym"
|
|
|
|
|
"debug/macho"
|
|
|
|
|
"debug/pe"
|
|
|
|
|
"flag"
|
|
|
|
|
"fmt"
|
|
|
|
|
"log"
|
|
|
|
|
"os"
|
|
|
|
|
"strconv"
|
2014-05-08 01:25:56 -04:00
|
|
|
"strings"
|
2014-04-14 10:58:49 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func printUsage(w *os.File) {
|
|
|
|
|
fmt.Fprintf(w, "usage: objdump binary start end\n")
|
|
|
|
|
fmt.Fprintf(w, "disassembles binary from start PC to end PC.\n")
|
2014-05-08 01:25:56 -04:00
|
|
|
fmt.Fprintf(w, "start and end are hexadecimal numbers with optional leading 0x prefix.\n")
|
2014-04-14 10:58:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func usage() {
|
|
|
|
|
printUsage(os.Stderr)
|
|
|
|
|
os.Exit(2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
log.SetFlags(0)
|
|
|
|
|
log.SetPrefix("objdump: ")
|
|
|
|
|
|
|
|
|
|
flag.Usage = usage
|
|
|
|
|
flag.Parse()
|
|
|
|
|
if flag.NArg() != 3 {
|
|
|
|
|
usage()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f, err := os.Open(flag.Arg(0))
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
textStart, textData, symtab, pclntab, err := loadTables(f)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("reading %s: %v", flag.Arg(0), err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pcln := gosym.NewLineTable(pclntab, textStart)
|
|
|
|
|
tab, err := gosym.NewTable(symtab, pcln)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("reading %s: %v", flag.Arg(0), err)
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-08 01:25:56 -04:00
|
|
|
start, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(1), "0x"), 16, 64)
|
2014-04-14 10:58:49 -04:00
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("invalid start PC: %v", err)
|
|
|
|
|
}
|
2014-05-08 01:25:56 -04:00
|
|
|
end, err := strconv.ParseUint(strings.TrimPrefix(flag.Arg(2), "0x"), 16, 64)
|
2014-04-14 10:58:49 -04:00
|
|
|
if err != nil {
|
|
|
|
|
log.Fatalf("invalid end PC: %v", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stdout := bufio.NewWriter(os.Stdout)
|
|
|
|
|
|
|
|
|
|
// For now, find spans of same PC/line/fn and
|
|
|
|
|
// emit them as having dummy instructions.
|
|
|
|
|
var (
|
|
|
|
|
spanPC uint64
|
|
|
|
|
spanFile string
|
|
|
|
|
spanLine int
|
|
|
|
|
spanFn *gosym.Func
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
flush := func(endPC uint64) {
|
|
|
|
|
if spanPC == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
fmt.Fprintf(stdout, "%s:%d\n", spanFile, spanLine)
|
|
|
|
|
for pc := spanPC; pc < endPC; pc++ {
|
|
|
|
|
// TODO(rsc): Disassemble instructions here.
|
|
|
|
|
if textStart <= pc && pc-textStart < uint64(len(textData)) {
|
|
|
|
|
fmt.Fprintf(stdout, " %x: byte %#x\n", pc, textData[pc-textStart])
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Fprintf(stdout, " %x: ?\n", pc)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
spanPC = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for pc := start; pc < end; pc++ {
|
|
|
|
|
file, line, fn := tab.PCToLine(pc)
|
|
|
|
|
if file != spanFile || line != spanLine || fn != spanFn {
|
|
|
|
|
flush(pc)
|
|
|
|
|
spanPC, spanFile, spanLine, spanFn = pc, file, line, fn
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
flush(end)
|
|
|
|
|
|
|
|
|
|
stdout.Flush()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func loadTables(f *os.File) (textStart uint64, textData, symtab, pclntab []byte, err error) {
|
|
|
|
|
if obj, err := elf.NewFile(f); err == nil {
|
|
|
|
|
if sect := obj.Section(".text"); sect != nil {
|
|
|
|
|
textStart = sect.Addr
|
|
|
|
|
textData, _ = sect.Data()
|
|
|
|
|
}
|
|
|
|
|
if sect := obj.Section(".gosymtab"); sect != nil {
|
|
|
|
|
if symtab, err = sect.Data(); err != nil {
|
|
|
|
|
return 0, nil, nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if sect := obj.Section(".gopclntab"); sect != nil {
|
|
|
|
|
if pclntab, err = sect.Data(); err != nil {
|
|
|
|
|
return 0, nil, nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return textStart, textData, symtab, pclntab, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if obj, err := macho.NewFile(f); err == nil {
|
|
|
|
|
if sect := obj.Section("__text"); sect != nil {
|
|
|
|
|
textStart = sect.Addr
|
|
|
|
|
textData, _ = sect.Data()
|
|
|
|
|
}
|
|
|
|
|
if sect := obj.Section("__gosymtab"); sect != nil {
|
|
|
|
|
if symtab, err = sect.Data(); err != nil {
|
|
|
|
|
return 0, nil, nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if sect := obj.Section("__gopclntab"); sect != nil {
|
|
|
|
|
if pclntab, err = sect.Data(); err != nil {
|
|
|
|
|
return 0, nil, nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return textStart, textData, symtab, pclntab, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if obj, err := pe.NewFile(f); err == nil {
|
|
|
|
|
if sect := obj.Section(".text"); sect != nil {
|
|
|
|
|
textStart = uint64(sect.VirtualAddress)
|
|
|
|
|
textData, _ = sect.Data()
|
|
|
|
|
}
|
|
|
|
|
if sect := obj.Section(".gosymtab"); sect != nil {
|
|
|
|
|
if symtab, err = sect.Data(); err != nil {
|
|
|
|
|
return 0, nil, nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if sect := obj.Section(".gopclntab"); sect != nil {
|
|
|
|
|
if pclntab, err = sect.Data(); err != nil {
|
|
|
|
|
return 0, nil, nil, nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return textStart, textData, symtab, pclntab, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0, nil, nil, nil, fmt.Errorf("unrecognized binary format")
|
|
|
|
|
}
|