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 }