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