github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/src/cmd/internal/objfile/disasm.go (about)

     1  // Copyright 2014 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  package objfile
     6  
     7  import (
     8  	"bufio"
     9  	"debug/gosym"
    10  	"encoding/binary"
    11  	"fmt"
    12  	"io"
    13  	"regexp"
    14  	"sort"
    15  	"strings"
    16  	"text/tabwriter"
    17  
    18  	"golang.org/x/arch/arm/armasm"
    19  	"golang.org/x/arch/ppc64/ppc64asm"
    20  	"golang.org/x/arch/x86/x86asm"
    21  )
    22  
    23  // Disasm is a disassembler for a given File.
    24  type Disasm struct {
    25  	syms      []Sym            //symbols in file, sorted by address
    26  	pcln      Liner            // pcln table
    27  	text      []byte           // bytes of text segment (actual instructions)
    28  	textStart uint64           // start PC of text
    29  	textEnd   uint64           // end PC of text
    30  	goarch    string           // GOARCH string
    31  	disasm    disasmFunc       // disassembler function for goarch
    32  	byteOrder binary.ByteOrder // byte order for goarch
    33  }
    34  
    35  // Disasm returns a disassembler for the file f.
    36  func (f *File) Disasm() (*Disasm, error) {
    37  	syms, err := f.Symbols()
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	pcln, err := f.PCLineTable()
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	textStart, textBytes, err := f.Text()
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	goarch := f.GOARCH()
    53  	disasm := disasms[goarch]
    54  	byteOrder := byteOrders[goarch]
    55  	if disasm == nil || byteOrder == nil {
    56  		return nil, fmt.Errorf("unsupported architecture")
    57  	}
    58  
    59  	// Filter out section symbols, overwriting syms in place.
    60  	keep := syms[:0]
    61  	for _, sym := range syms {
    62  		switch sym.Name {
    63  		case "runtime.text", "text", "_text", "runtime.etext", "etext", "_etext":
    64  			// drop
    65  		default:
    66  			keep = append(keep, sym)
    67  		}
    68  	}
    69  	syms = keep
    70  	d := &Disasm{
    71  		syms:      syms,
    72  		pcln:      pcln,
    73  		text:      textBytes,
    74  		textStart: textStart,
    75  		textEnd:   textStart + uint64(len(textBytes)),
    76  		goarch:    goarch,
    77  		disasm:    disasm,
    78  		byteOrder: byteOrder,
    79  	}
    80  
    81  	return d, nil
    82  }
    83  
    84  // lookup finds the symbol name containing addr.
    85  func (d *Disasm) lookup(addr uint64) (name string, base uint64) {
    86  	i := sort.Search(len(d.syms), func(i int) bool { return addr < d.syms[i].Addr })
    87  	if i > 0 {
    88  		s := d.syms[i-1]
    89  		if s.Addr != 0 && s.Addr <= addr && addr < s.Addr+uint64(s.Size) {
    90  			return s.Name, s.Addr
    91  		}
    92  	}
    93  	return "", 0
    94  }
    95  
    96  // base returns the final element in the path.
    97  // It works on both Windows and Unix paths,
    98  // regardless of host operating system.
    99  func base(path string) string {
   100  	path = path[strings.LastIndex(path, "/")+1:]
   101  	path = path[strings.LastIndex(path, `\`)+1:]
   102  	return path
   103  }
   104  
   105  // Print prints a disassembly of the file to w.
   106  // If filter is non-nil, the disassembly only includes functions with names matching filter.
   107  // The disassembly only includes functions that overlap the range [start, end).
   108  func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64) {
   109  	if start < d.textStart {
   110  		start = d.textStart
   111  	}
   112  	if end > d.textEnd {
   113  		end = d.textEnd
   114  	}
   115  	printed := false
   116  	bw := bufio.NewWriter(w)
   117  	for _, sym := range d.syms {
   118  		symStart := sym.Addr
   119  		symEnd := sym.Addr + uint64(sym.Size)
   120  		relocs := sym.Relocs
   121  		if sym.Code != 'T' && sym.Code != 't' ||
   122  			symStart < d.textStart ||
   123  			symEnd <= start || end <= symStart ||
   124  			filter != nil && !filter.MatchString(sym.Name) {
   125  			continue
   126  		}
   127  		if printed {
   128  			fmt.Fprintf(bw, "\n")
   129  		}
   130  		printed = true
   131  
   132  		file, _, _ := d.pcln.PCToLine(sym.Addr)
   133  		fmt.Fprintf(bw, "TEXT %s(SB) %s\n", sym.Name, file)
   134  
   135  		tw := tabwriter.NewWriter(bw, 1, 8, 1, '\t', 0)
   136  		if symEnd > end {
   137  			symEnd = end
   138  		}
   139  		code := d.text[:end-d.textStart]
   140  		d.Decode(symStart, symEnd, relocs, func(pc, size uint64, file string, line int, text string) {
   141  			i := pc - d.textStart
   142  			fmt.Fprintf(tw, "\t%s:%d\t%#x\t", base(file), line, pc)
   143  			if size%4 != 0 || d.goarch == "386" || d.goarch == "amd64" {
   144  				// Print instruction as bytes.
   145  				fmt.Fprintf(tw, "%x", code[i:i+size])
   146  			} else {
   147  				// Print instruction as 32-bit words.
   148  				for j := uint64(0); j < size; j += 4 {
   149  					if j > 0 {
   150  						fmt.Fprintf(tw, " ")
   151  					}
   152  					fmt.Fprintf(tw, "%08x", d.byteOrder.Uint32(code[i+j:]))
   153  				}
   154  			}
   155  			fmt.Fprintf(tw, "\t%s\n", text)
   156  		})
   157  		tw.Flush()
   158  	}
   159  	bw.Flush()
   160  }
   161  
   162  // Decode disassembles the text segment range [start, end), calling f for each instruction.
   163  func (d *Disasm) Decode(start, end uint64, relocs []Reloc, f func(pc, size uint64, file string, line int, text string)) {
   164  	if start < d.textStart {
   165  		start = d.textStart
   166  	}
   167  	if end > d.textEnd {
   168  		end = d.textEnd
   169  	}
   170  	code := d.text[:end-d.textStart]
   171  	lookup := d.lookup
   172  	for pc := start; pc < end; {
   173  		i := pc - d.textStart
   174  		text, size := d.disasm(code[i:], pc, lookup, d.byteOrder)
   175  		file, line, _ := d.pcln.PCToLine(pc)
   176  		text += "\t"
   177  		first := true
   178  		for len(relocs) > 0 && relocs[0].Addr < i+uint64(size) {
   179  			if first {
   180  				first = false
   181  			} else {
   182  				text += " "
   183  			}
   184  			text += relocs[0].Stringer.String(pc - start)
   185  			relocs = relocs[1:]
   186  		}
   187  		f(pc, uint64(size), file, line, text)
   188  		pc += uint64(size)
   189  	}
   190  }
   191  
   192  type lookupFunc func(addr uint64) (sym string, base uint64)
   193  type disasmFunc func(code []byte, pc uint64, lookup lookupFunc, ord binary.ByteOrder) (text string, size int)
   194  
   195  func disasm_386(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder) (string, int) {
   196  	return disasm_x86(code, pc, lookup, 32)
   197  }
   198  
   199  func disasm_amd64(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder) (string, int) {
   200  	return disasm_x86(code, pc, lookup, 64)
   201  }
   202  
   203  func disasm_x86(code []byte, pc uint64, lookup lookupFunc, arch int) (string, int) {
   204  	inst, err := x86asm.Decode(code, 64)
   205  	var text string
   206  	size := inst.Len
   207  	if err != nil || size == 0 || inst.Op == 0 {
   208  		size = 1
   209  		text = "?"
   210  	} else {
   211  		text = x86asm.GoSyntax(inst, pc, lookup)
   212  	}
   213  	return text, size
   214  }
   215  
   216  type textReader struct {
   217  	code []byte
   218  	pc   uint64
   219  }
   220  
   221  func (r textReader) ReadAt(data []byte, off int64) (n int, err error) {
   222  	if off < 0 || uint64(off) < r.pc {
   223  		return 0, io.EOF
   224  	}
   225  	d := uint64(off) - r.pc
   226  	if d >= uint64(len(r.code)) {
   227  		return 0, io.EOF
   228  	}
   229  	n = copy(data, r.code[d:])
   230  	if n < len(data) {
   231  		err = io.ErrUnexpectedEOF
   232  	}
   233  	return
   234  }
   235  
   236  func disasm_arm(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder) (string, int) {
   237  	inst, err := armasm.Decode(code, armasm.ModeARM)
   238  	var text string
   239  	size := inst.Len
   240  	if err != nil || size == 0 || inst.Op == 0 {
   241  		size = 4
   242  		text = "?"
   243  	} else {
   244  		text = armasm.GoSyntax(inst, pc, lookup, textReader{code, pc})
   245  	}
   246  	return text, size
   247  }
   248  
   249  func disasm_ppc64(code []byte, pc uint64, lookup lookupFunc, byteOrder binary.ByteOrder) (string, int) {
   250  	inst, err := ppc64asm.Decode(code, byteOrder)
   251  	var text string
   252  	size := inst.Len
   253  	if err != nil || size == 0 || inst.Op == 0 {
   254  		size = 4
   255  		text = "?"
   256  	} else {
   257  		text = ppc64asm.GoSyntax(inst, pc, lookup)
   258  	}
   259  	return text, size
   260  }
   261  
   262  var disasms = map[string]disasmFunc{
   263  	"386":     disasm_386,
   264  	"amd64":   disasm_amd64,
   265  	"arm":     disasm_arm,
   266  	"ppc64":   disasm_ppc64,
   267  	"ppc64le": disasm_ppc64,
   268  }
   269  
   270  var byteOrders = map[string]binary.ByteOrder{
   271  	"386":     binary.LittleEndian,
   272  	"amd64":   binary.LittleEndian,
   273  	"arm":     binary.LittleEndian,
   274  	"ppc64":   binary.BigEndian,
   275  	"ppc64le": binary.LittleEndian,
   276  	"s390x":   binary.BigEndian,
   277  }
   278  
   279  type Liner interface {
   280  	// Given a pc, returns the corresponding file, line, and function data.
   281  	// If unknown, returns "",0,nil.
   282  	PCToLine(uint64) (string, int, *gosym.Func)
   283  }