aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/internal/objfile
diff options
context:
space:
mode:
authorKeith Randall <khr@golang.org>2016-07-02 17:19:25 -0700
committerKeith Randall <khr@golang.org>2016-08-24 17:36:59 +0000
commita99f812cba4c5a5207fed9be5488312a44a5df34 (patch)
treed529f4e86495db40844a6640477deefea4f7c349 /src/cmd/internal/objfile
parent873dca4c17437d07ae97ef4f9e9a8e8c93d88bd7 (diff)
downloadgo-a99f812cba4c5a5207fed9be5488312a44a5df34.tar.xz
cmd/objdump: implement objdump of .o files
Update goobj reader so it can provide all the information necessary to disassemble .o (and .a) files. Grab architecture of .o files from header. .o files have relocations in them. This CL also contains a simple mechanism to disassemble relocations and add relocation info as an extra column in the output. Fixes #13862 Change-Id: I608fd253ff1522ea47f18be650b38d528dae9054 Reviewed-on: https://go-review.googlesource.com/24818 Reviewed-by: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/cmd/internal/objfile')
-rw-r--r--src/cmd/internal/objfile/disasm.go24
-rw-r--r--src/cmd/internal/objfile/goobj.go71
-rw-r--r--src/cmd/internal/objfile/objfile.go31
3 files changed, 107 insertions, 19 deletions
diff --git a/src/cmd/internal/objfile/disasm.go b/src/cmd/internal/objfile/disasm.go
index 25c3301ab8..771187bfe4 100644
--- a/src/cmd/internal/objfile/disasm.go
+++ b/src/cmd/internal/objfile/disasm.go
@@ -22,7 +22,7 @@ import (
// Disasm is a disassembler for a given File.
type Disasm struct {
syms []Sym //symbols in file, sorted by address
- pcln *gosym.Table // pcln table
+ pcln Liner // pcln table
text []byte // bytes of text segment (actual instructions)
textStart uint64 // start PC of text
textEnd uint64 // end PC of text
@@ -116,6 +116,7 @@ func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64) {
for _, sym := range d.syms {
symStart := sym.Addr
symEnd := sym.Addr + uint64(sym.Size)
+ relocs := sym.Relocs
if sym.Code != 'T' && sym.Code != 't' ||
symStart < d.textStart ||
symEnd <= start || end <= symStart ||
@@ -135,7 +136,7 @@ func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64) {
symEnd = end
}
code := d.text[:end-d.textStart]
- d.Decode(symStart, symEnd, func(pc, size uint64, file string, line int, text string) {
+ d.Decode(symStart, symEnd, relocs, func(pc, size uint64, file string, line int, text string) {
i := pc - d.textStart
fmt.Fprintf(tw, "\t%s:%d\t%#x\t", base(file), line, pc)
if size%4 != 0 || d.goarch == "386" || d.goarch == "amd64" {
@@ -158,7 +159,7 @@ func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64) {
}
// Decode disassembles the text segment range [start, end), calling f for each instruction.
-func (d *Disasm) Decode(start, end uint64, f func(pc, size uint64, file string, line int, text string)) {
+func (d *Disasm) Decode(start, end uint64, relocs []Reloc, f func(pc, size uint64, file string, line int, text string)) {
if start < d.textStart {
start = d.textStart
}
@@ -171,6 +172,17 @@ func (d *Disasm) Decode(start, end uint64, f func(pc, size uint64, file string,
i := pc - d.textStart
text, size := d.disasm(code[i:], pc, lookup)
file, line, _ := d.pcln.PCToLine(pc)
+ text += "\t"
+ first := true
+ for len(relocs) > 0 && relocs[0].Addr < i+uint64(size) {
+ if first {
+ first = false
+ } else {
+ text += " "
+ }
+ text += relocs[0].Stringer.String(pc - start)
+ relocs = relocs[1:]
+ }
f(pc, uint64(size), file, line, text)
pc += uint64(size)
}
@@ -247,3 +259,9 @@ var byteOrders = map[string]binary.ByteOrder{
"ppc64le": binary.LittleEndian,
"s390x": binary.BigEndian,
}
+
+type Liner interface {
+ // Given a pc, returns the corresponding file, line, and function data.
+ // If unknown, returns "",0,nil.
+ PCToLine(uint64) (string, int, *gosym.Func)
+}
diff --git a/src/cmd/internal/objfile/goobj.go b/src/cmd/internal/objfile/goobj.go
index 43435efc68..230137e0f5 100644
--- a/src/cmd/internal/objfile/goobj.go
+++ b/src/cmd/internal/objfile/goobj.go
@@ -8,7 +8,9 @@ package objfile
import (
"cmd/internal/goobj"
+ "cmd/internal/sys"
"debug/dwarf"
+ "debug/gosym"
"errors"
"fmt"
"os"
@@ -16,6 +18,7 @@ import (
type goobjFile struct {
goobj *goobj.Package
+ f *os.File // the underlying .o or .a file
}
func openGoobj(r *os.File) (rawFile, error) {
@@ -23,7 +26,7 @@ func openGoobj(r *os.File) (rawFile, error) {
if err != nil {
return nil, err
}
- return &goobjFile{f}, nil
+ return &goobjFile{goobj: f, f: r}, nil
}
func goobjName(id goobj.SymID) string {
@@ -55,6 +58,9 @@ func (f *goobjFile) symbols() ([]Sym, error) {
if s.Version != 0 {
sym.Code += 'a' - 'A'
}
+ for i, r := range s.Reloc {
+ sym.Relocs = append(sym.Relocs, Reloc{Addr: uint64(s.Data.Offset) + uint64(r.Offset), Size: uint64(r.Size), Stringer: &s.Reloc[i]})
+ }
syms = append(syms, sym)
}
@@ -75,23 +81,68 @@ func (f *goobjFile) symbols() ([]Sym, error) {
return syms, nil
}
-// pcln does not make sense for Go object files, because each
-// symbol has its own individual pcln table, so there is no global
-// space of addresses to map.
func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
+ // Should never be called. We implement Liner below, callers
+ // should use that instead.
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.
+// Find returns the file name, line, and function data for the given pc.
+// Returns "",0,nil if unknown.
+// This function implements the Liner interface in preference to pcln() above.
+func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) {
+ // TODO: this is really inefficient. Binary search? Memoize last result?
+ var arch *sys.Arch
+ for _, a := range sys.Archs {
+ if a.Name == f.goobj.Arch {
+ arch = a
+ break
+ }
+ }
+ if arch == nil {
+ return "", 0, nil
+ }
+ for _, s := range f.goobj.Syms {
+ if pc < uint64(s.Data.Offset) || pc >= uint64(s.Data.Offset+s.Data.Size) {
+ continue
+ }
+ if s.Func == nil {
+ return "", 0, nil
+ }
+ pcfile := make([]byte, s.Func.PCFile.Size)
+ _, err := f.f.ReadAt(pcfile, s.Func.PCFile.Offset)
+ if err != nil {
+ return "", 0, nil
+ }
+ fileID := gosym.PCValue(pcfile, pc-uint64(s.Data.Offset), arch.MinLC)
+ fileName := s.Func.File[fileID]
+ pcline := make([]byte, s.Func.PCLine.Size)
+ _, err = f.f.ReadAt(pcline, s.Func.PCLine.Offset)
+ if err != nil {
+ return "", 0, nil
+ }
+ line := gosym.PCValue(pcline, pc-uint64(s.Data.Offset), arch.MinLC)
+ // Note: we provide only the name in the Func structure.
+ // We could provide more if needed.
+ return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: s.Name}}
+ }
+ return "", 0, nil
+}
+
+// We treat the whole object file as the text section.
func (f *goobjFile) text() (textStart uint64, text []byte, err error) {
- return 0, nil, fmt.Errorf("text not available in go object file")
+ var info os.FileInfo
+ info, err = f.f.Stat()
+ if err != nil {
+ return
+ }
+ text = make([]byte, info.Size())
+ _, err = f.f.ReadAt(text, 0)
+ return
}
-// 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"
+ return f.goobj.Arch
}
func (f *goobjFile) loadAddress() (uint64, error) {
diff --git a/src/cmd/internal/objfile/objfile.go b/src/cmd/internal/objfile/objfile.go
index e5d99f086b..2bf6363f29 100644
--- a/src/cmd/internal/objfile/objfile.go
+++ b/src/cmd/internal/objfile/objfile.go
@@ -30,11 +30,24 @@ type File struct {
// A Sym is a symbol defined in an executable file.
type Sym struct {
- Name string // symbol name
- Addr uint64 // virtual address of symbol
- Size int64 // size in bytes
- Code rune // nm code (T for text, D for data, and so on)
- Type string // XXX?
+ Name string // symbol name
+ Addr uint64 // virtual address of symbol
+ Size int64 // size in bytes
+ Code rune // nm code (T for text, D for data, and so on)
+ Type string // XXX?
+ Relocs []Reloc // in increasing Addr order
+}
+
+type Reloc struct {
+ Addr uint64 // Address of first byte that reloc applies to.
+ Size uint64 // Number of bytes
+ Stringer RelocStringer
+}
+
+type RelocStringer interface {
+ // insnOffset is the offset of the instruction containing the relocation
+ // from the start of the symbol containing the relocation.
+ String(insnOffset uint64) string
}
var openers = []func(*os.File) (rawFile, error){
@@ -80,7 +93,13 @@ func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr }
func (x byAddr) Len() int { return len(x) }
func (x byAddr) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-func (f *File) PCLineTable() (*gosym.Table, error) {
+func (f *File) PCLineTable() (Liner, error) {
+ // If the raw file implements Liner directly, use that.
+ // Currently, only Go intermediate objects and archives (goobj) use this path.
+ if pcln, ok := f.raw.(Liner); ok {
+ return pcln, nil
+ }
+ // Otherwise, read the pcln tables and build a Liner out of that.
textStart, symtab, pclntab, err := f.raw.pcln()
if err != nil {
return nil, err