Commit 3eadbb02 authored by Russ Cox's avatar Russ Cox

cmd/objdump: use cmd/internal/objfile

This removes a bunch of ugly duplicate code.
The end goal is to factor the disassembly code
into cmd/internal/objfile too, so that pprof can use it,
but one step at a time.

LGTM=r, iant
R=r, alex.brainman, iant
CC=golang-codereviews
https://golang.org/cl/149400043
parent 97b24a05
......@@ -8,6 +8,7 @@ package objfile
import (
"debug/elf"
"fmt"
"os"
)
......@@ -77,3 +78,27 @@ func (f *elfFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
}
return textStart, symtab, pclntab, nil
}
func (f *elfFile) text() (textStart uint64, text []byte, err error) {
sect := f.elf.Section(".text")
if sect == nil {
return 0, nil, fmt.Errorf("text section not found")
}
textStart = sect.Addr
text, err = sect.Data()
return
}
func (f *elfFile) goarch() string {
switch f.elf.Machine {
case elf.EM_386:
return "386"
case elf.EM_X86_64:
return "amd64"
case elf.EM_ARM:
return "arm"
case elf.EM_PPC64:
return "power64"
}
return ""
}
......@@ -79,3 +79,15 @@ func (f *goobjFile) symbols() ([]Sym, error) {
func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
}
// text does not make sense for Go object files, because
// each function has a separate section.
func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
return 0, nil, fmt.Errorf("text not available in go object file")
}
// goarch makes sense but is not exposed in debug/goobj's API,
// and we don't need it yet for any users of internal/objfile.
func (f *goobjFile) goarch() string {
return "GOARCH unimplemented for debug/goobj files"
}
......@@ -85,6 +85,30 @@ func (f *machoFile) pcln() (textStart uint64, symtab, pclntab []byte, err error)
return textStart, symtab, pclntab, nil
}
func (f *machoFile) text() (textStart uint64, text []byte, err error) {
sect := f.macho.Section("__text")
if sect == nil {
return 0, nil, fmt.Errorf("text section not found")
}
textStart = sect.Addr
text, err = sect.Data()
return
}
func (f *machoFile) goarch() string {
switch f.macho.Cpu {
case macho.Cpu386:
return "386"
case macho.CpuAmd64:
return "amd64"
case macho.CpuArm:
return "arm"
case macho.CpuPpc64:
return "power64"
}
return ""
}
type uint64s []uint64
func (x uint64s) Len() int { return len(x) }
......
......@@ -14,6 +14,8 @@ import (
type rawFile interface {
symbols() (syms []Sym, err error)
pcln() (textStart uint64, symtab, pclntab []byte, err error)
text() (textStart uint64, text []byte, err error)
goarch() string
}
// A File is an opened executable file.
......@@ -70,3 +72,11 @@ func (f *File) PCLineTable() (*gosym.Table, error) {
}
return gosym.NewTable(symtab, gosym.NewLineTable(pclntab, textStart))
}
func (f *File) Text() (uint64, []byte, error) {
return f.raw.text()
}
func (f *File) GOARCH() string {
return f.raw.goarch()
}
......@@ -133,6 +133,25 @@ func (f *peFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
return textStart, symtab, pclntab, nil
}
func (f *peFile) text() (textStart uint64, text []byte, err error) {
var imageBase uint64
switch oh := f.pe.OptionalHeader.(type) {
case *pe.OptionalHeader32:
imageBase = uint64(oh.ImageBase)
case *pe.OptionalHeader64:
imageBase = oh.ImageBase
default:
return 0, nil, fmt.Errorf("pe file format not recognized")
}
sect := f.pe.Section(".text")
if sect == nil {
return 0, nil, fmt.Errorf("text section not found")
}
textStart = imageBase + uint64(sect.VirtualAddress)
text, err = sect.Data()
return
}
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
for _, s := range f.Symbols {
if s.Name != name {
......@@ -168,3 +187,15 @@ func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
}
return data[ssym.Value:esym.Value], nil
}
func (f *peFile) goarch() string {
// Not sure how to get the info we want from PE header.
// Look in symbol table for telltale rt0 symbol.
if _, err := findPESymbol(f.pe, "_rt0_386_windows"); err == nil {
return "386"
}
if _, err := findPESymbol(f.pe, "_rt0_amd64_windows"); err == nil {
return "amd64"
}
return ""
}
......@@ -88,6 +88,16 @@ func (f *plan9File) pcln() (textStart uint64, symtab, pclntab []byte, err error)
return textStart, symtab, pclntab, nil
}
func (f *plan9File) text() (textStart uint64, text []byte, err error) {
sect := f.plan9.Section("text")
if sect == nil {
return 0, nil, fmt.Errorf("text section not found")
}
textStart = f.plan9.LoadAddress + f.plan9.HdrSize
text, err = sect.Data()
return
}
func findPlan9Symbol(f *plan9obj.File, name string) (*plan9obj.Sym, error) {
syms, err := f.Symbols()
if err != nil {
......@@ -122,3 +132,15 @@ func loadPlan9Table(f *plan9obj.File, sname, ename string) ([]byte, error) {
textStart := f.LoadAddress + f.HdrSize
return data[ssym.Value-textStart : esym.Value-textStart], nil
}
func (f *plan9File) goarch() string {
switch f.plan9.Magic {
case plan9obj.Magic386:
return "386"
case plan9obj.MagicAMD64:
return "amd64"
case plan9obj.MagicARM:
return "arm"
}
return ""
}
all: x86.go armasm.go
x86.go: bundle
./bundle -p main -x x86_ rsc.io/x86/x86asm | gofmt >x86.go
armasm.go: bundle
./bundle -p main -x arm_ rsc.io/arm/armasm | gofmt >armasm.go
bundle:
go build -o bundle code.google.com/p/rsc/cmd/bundle
// Copyright 2013 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.
// Parsing of ELF executables (Linux, FreeBSD, and so on).
package main
import (
"debug/elf"
"os"
)
func elfSymbols(f *os.File) (syms []Sym, goarch string) {
p, err := elf.NewFile(f)
if err != nil {
errorf("parsing %s: %v", f.Name(), err)
return
}
elfSyms, err := p.Symbols()
if err != nil {
errorf("parsing %s: %v", f.Name(), err)
return
}
switch p.Machine {
case elf.EM_X86_64:
goarch = "amd64"
case elf.EM_386:
goarch = "386"
case elf.EM_ARM:
goarch = "arm"
}
for _, s := range elfSyms {
sym := Sym{Addr: s.Value, Name: s.Name, Size: int64(s.Size), Code: '?'}
switch s.Section {
case elf.SHN_UNDEF:
sym.Code = 'U'
case elf.SHN_COMMON:
sym.Code = 'B'
default:
i := int(s.Section)
if i < 0 || i >= len(p.Sections) {
break
}
sect := p.Sections[i]
switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) {
case elf.SHF_ALLOC | elf.SHF_EXECINSTR:
sym.Code = 'T'
case elf.SHF_ALLOC:
sym.Code = 'R'
case elf.SHF_ALLOC | elf.SHF_WRITE:
sym.Code = 'D'
}
}
if elf.ST_BIND(s.Info) == elf.STB_LOCAL {
sym.Code += 'a' - 'A'
}
syms = append(syms, sym)
}
return
}
// Copyright 2013 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.
// Parsing of Mach-O executables (OS X).
package main
import (
"debug/macho"
"os"
"sort"
)
func machoSymbols(f *os.File) (syms []Sym, goarch string) {
p, err := macho.NewFile(f)
if err != nil {
errorf("parsing %s: %v", f.Name(), err)
return
}
if p.Symtab == nil {
errorf("%s: no symbol table", f.Name())
return
}
switch p.Cpu {
case macho.Cpu386:
goarch = "386"
case macho.CpuAmd64:
goarch = "amd64"
case macho.CpuArm:
goarch = "arm"
}
// Build sorted list of addresses of all symbols.
// We infer the size of a symbol by looking at where the next symbol begins.
var addrs []uint64
for _, s := range p.Symtab.Syms {
addrs = append(addrs, s.Value)
}
sort.Sort(uint64s(addrs))
for _, s := range p.Symtab.Syms {
sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'}
i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
if i < len(addrs) {
sym.Size = int64(addrs[i] - s.Value)
}
if s.Sect == 0 {
sym.Code = 'U'
} else if int(s.Sect) <= len(p.Sections) {
sect := p.Sections[s.Sect-1]
switch sect.Seg {
case "__TEXT":
sym.Code = 'R'
case "__DATA":
sym.Code = 'D'
}
switch sect.Seg + " " + sect.Name {
case "__TEXT __text":
sym.Code = 'T'
case "__DATA __bss", "__DATA __noptrbss":
sym.Code = 'B'
}
}
syms = append(syms, sym)
}
return
}
type uint64s []uint64
func (x uint64s) Len() int { return len(x) }
func (x uint64s) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x uint64s) Less(i, j int) bool { return x[i] < x[j] }
This diff is collapsed.
// Copyright 2013 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.
// Parsing of PE executables (Microsoft Windows).
package main
import (
"debug/pe"
"os"
"sort"
)
func peSymbols(f *os.File) (syms []Sym, goarch string) {
p, err := pe.NewFile(f)
if err != nil {
errorf("parsing %s: %v", f.Name(), err)
return
}
// Build sorted list of addresses of all symbols.
// We infer the size of a symbol by looking at where the next symbol begins.
var addrs []uint64
var imageBase uint64
switch oh := p.OptionalHeader.(type) {
case *pe.OptionalHeader32:
imageBase = uint64(oh.ImageBase)
goarch = "386"
case *pe.OptionalHeader64:
imageBase = oh.ImageBase
goarch = "amd64"
default:
errorf("parsing %s: file format not recognized", f.Name())
return
}
for _, s := range p.Symbols {
const (
N_UNDEF = 0 // An undefined (extern) symbol
N_ABS = -1 // An absolute symbol (e_value is a constant, not an address)
N_DEBUG = -2 // A debugging symbol
)
sym := Sym{Name: s.Name, Addr: uint64(s.Value), Code: '?'}
switch s.SectionNumber {
case N_UNDEF:
sym.Code = 'U'
case N_ABS:
sym.Code = 'C'
case N_DEBUG:
sym.Code = '?'
default:
if s.SectionNumber < 0 {
errorf("parsing %s: invalid section number %d", f.Name(), s.SectionNumber)
return
}
if len(p.Sections) < int(s.SectionNumber) {
errorf("parsing %s: section number %d is large then max %d", f.Name(), s.SectionNumber, len(p.Sections))
return
}
sect := p.Sections[s.SectionNumber-1]
const (
text = 0x20
data = 0x40
bss = 0x80
permX = 0x20000000
permR = 0x40000000
permW = 0x80000000
)
ch := sect.Characteristics
switch {
case ch&text != 0:
sym.Code = 'T'
case ch&data != 0:
if ch&permW == 0 {
sym.Code = 'R'
} else {
sym.Code = 'D'
}
case ch&bss != 0:
sym.Code = 'B'
}
sym.Addr += imageBase + uint64(sect.VirtualAddress)
}
syms = append(syms, sym)
addrs = append(addrs, sym.Addr)
}
sort.Sort(uint64s(addrs))
for i := range syms {
j := sort.Search(len(addrs), func(x int) bool { return addrs[x] > syms[i].Addr })
if j < len(addrs) {
syms[i].Size = int64(addrs[j] - syms[i].Addr)
}
}
return
}
// 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.
// Parsing of Plan 9 a.out executables.
package main
import (
"debug/plan9obj"
"os"
"sort"
)
var validSymType = map[rune]bool{
'T': true,
't': true,
'D': true,
'd': true,
'B': true,
'b': true,
}
func plan9Symbols(f *os.File) (syms []Sym, goarch string) {
p, err := plan9obj.NewFile(f)
if err != nil {
errorf("parsing %s: %v", f.Name(), err)
return
}
plan9Syms, err := p.Symbols()
if err != nil {
errorf("parsing %s: %v", f.Name(), err)
return
}
switch p.Magic {
case plan9obj.MagicAMD64:
goarch = "amd64"
case plan9obj.Magic386:
goarch = "386"
case plan9obj.MagicARM:
goarch = "arm"
}
// Build sorted list of addresses of all symbols.
// We infer the size of a symbol by looking at where the next symbol begins.
var addrs []uint64
for _, s := range plan9Syms {
if !validSymType[s.Type] {
continue
}
addrs = append(addrs, s.Value)
}
sort.Sort(uint64s(addrs))
for _, s := range plan9Syms {
if !validSymType[s.Type] {
continue
}
sym := Sym{Addr: s.Value, Name: s.Name, Code: rune(s.Type)}
i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
if i < len(addrs) {
sym.Size = int64(addrs[i] - s.Value)
}
syms = append(syms, sym)
}
return
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment