Source file src/pkg/cmd/internal/objfile/disasm.go
1
2
3
4
5 package objfile
6
7 import (
8 "bufio"
9 "bytes"
10 "cmd/internal/src"
11 "container/list"
12 "debug/gosym"
13 "encoding/binary"
14 "fmt"
15 "io"
16 "io/ioutil"
17 "os"
18 "path/filepath"
19 "regexp"
20 "sort"
21 "strings"
22 "text/tabwriter"
23
24 "golang.org/x/arch/arm/armasm"
25 "golang.org/x/arch/arm64/arm64asm"
26 "golang.org/x/arch/ppc64/ppc64asm"
27 "golang.org/x/arch/x86/x86asm"
28 )
29
30
31 type Disasm struct {
32 syms []Sym
33 pcln Liner
34 text []byte
35 textStart uint64
36 textEnd uint64
37 goarch string
38 disasm disasmFunc
39 byteOrder binary.ByteOrder
40 }
41
42
43 func (e *Entry) Disasm() (*Disasm, error) {
44 syms, err := e.Symbols()
45 if err != nil {
46 return nil, err
47 }
48
49 pcln, err := e.PCLineTable()
50 if err != nil {
51 return nil, err
52 }
53
54 textStart, textBytes, err := e.Text()
55 if err != nil {
56 return nil, err
57 }
58
59 goarch := e.GOARCH()
60 disasm := disasms[goarch]
61 byteOrder := byteOrders[goarch]
62 if disasm == nil || byteOrder == nil {
63 return nil, fmt.Errorf("unsupported architecture")
64 }
65
66
67 keep := syms[:0]
68 for _, sym := range syms {
69 switch sym.Name {
70 case "runtime.text", "text", "_text", "runtime.etext", "etext", "_etext":
71
72 default:
73 keep = append(keep, sym)
74 }
75 }
76 syms = keep
77 d := &Disasm{
78 syms: syms,
79 pcln: pcln,
80 text: textBytes,
81 textStart: textStart,
82 textEnd: textStart + uint64(len(textBytes)),
83 goarch: goarch,
84 disasm: disasm,
85 byteOrder: byteOrder,
86 }
87
88 return d, nil
89 }
90
91
92 func (d *Disasm) lookup(addr uint64) (name string, base uint64) {
93 i := sort.Search(len(d.syms), func(i int) bool { return addr < d.syms[i].Addr })
94 if i > 0 {
95 s := d.syms[i-1]
96 if s.Addr != 0 && s.Addr <= addr && addr < s.Addr+uint64(s.Size) {
97 return s.Name, s.Addr
98 }
99 }
100 return "", 0
101 }
102
103
104
105
106 func base(path string) string {
107 path = path[strings.LastIndex(path, "/")+1:]
108 path = path[strings.LastIndex(path, `\`)+1:]
109 return path
110 }
111
112
113 type CachedFile struct {
114 FileName string
115 Lines [][]byte
116 }
117
118
119 type FileCache struct {
120 files *list.List
121 maxLen int
122 }
123
124
125 func NewFileCache(maxLen int) *FileCache {
126 return &FileCache{
127 files: list.New(),
128 maxLen: maxLen,
129 }
130 }
131
132
133
134
135
136 func (fc *FileCache) Line(filename string, line int) ([]byte, error) {
137 if filepath.Ext(filename) != ".go" {
138 return nil, nil
139 }
140
141
142
143
144 filename = strings.TrimPrefix(filename, src.FileSymPrefix)
145
146
147 filename = filepath.Clean(os.ExpandEnv(filename))
148
149 var cf *CachedFile
150 var e *list.Element
151
152 for e = fc.files.Front(); e != nil; e = e.Next() {
153 cf = e.Value.(*CachedFile)
154 if cf.FileName == filename {
155 break
156 }
157 }
158
159 if e == nil {
160 content, err := ioutil.ReadFile(filename)
161 if err != nil {
162 return nil, err
163 }
164
165 cf = &CachedFile{
166 FileName: filename,
167 Lines: bytes.Split(content, []byte{'\n'}),
168 }
169 fc.files.PushFront(cf)
170
171 if fc.files.Len() >= fc.maxLen {
172 fc.files.Remove(fc.files.Back())
173 }
174 } else {
175 fc.files.MoveToFront(e)
176 }
177
178 return cf.Lines[line-1], nil
179 }
180
181
182
183
184
185 func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64, printCode bool) {
186 if start < d.textStart {
187 start = d.textStart
188 }
189 if end > d.textEnd {
190 end = d.textEnd
191 }
192 printed := false
193 bw := bufio.NewWriter(w)
194
195 var fc *FileCache
196 if printCode {
197 fc = NewFileCache(8)
198 }
199
200 tw := tabwriter.NewWriter(bw, 18, 8, 1, '\t', tabwriter.StripEscape)
201 for _, sym := range d.syms {
202 symStart := sym.Addr
203 symEnd := sym.Addr + uint64(sym.Size)
204 relocs := sym.Relocs
205 if sym.Code != 'T' && sym.Code != 't' ||
206 symStart < d.textStart ||
207 symEnd <= start || end <= symStart ||
208 filter != nil && !filter.MatchString(sym.Name) {
209 continue
210 }
211 if printed {
212 fmt.Fprintf(bw, "\n")
213 }
214 printed = true
215
216 file, _, _ := d.pcln.PCToLine(sym.Addr)
217 fmt.Fprintf(bw, "TEXT %s(SB) %s\n", sym.Name, file)
218
219 if symEnd > end {
220 symEnd = end
221 }
222 code := d.text[:end-d.textStart]
223
224 var lastFile string
225 var lastLine int
226
227 d.Decode(symStart, symEnd, relocs, func(pc, size uint64, file string, line int, text string) {
228 i := pc - d.textStart
229
230 if printCode {
231 if file != lastFile || line != lastLine {
232 if srcLine, err := fc.Line(file, line); err == nil {
233 fmt.Fprintf(tw, "%s%s%s\n", []byte{tabwriter.Escape}, srcLine, []byte{tabwriter.Escape})
234 }
235
236 lastFile, lastLine = file, line
237 }
238
239 fmt.Fprintf(tw, " %#x\t", pc)
240 } else {
241 fmt.Fprintf(tw, " %s:%d\t%#x\t", base(file), line, pc)
242 }
243
244 if size%4 != 0 || d.goarch == "386" || d.goarch == "amd64" || d.goarch == "amd64p32" {
245
246 fmt.Fprintf(tw, "%x", code[i:i+size])
247 } else {
248
249 for j := uint64(0); j < size; j += 4 {
250 if j > 0 {
251 fmt.Fprintf(tw, " ")
252 }
253 fmt.Fprintf(tw, "%08x", d.byteOrder.Uint32(code[i+j:]))
254 }
255 }
256 fmt.Fprintf(tw, "\t%s\t\n", text)
257 })
258 tw.Flush()
259 }
260 bw.Flush()
261 }
262
263
264 func (d *Disasm) Decode(start, end uint64, relocs []Reloc, f func(pc, size uint64, file string, line int, text string)) {
265 if start < d.textStart {
266 start = d.textStart
267 }
268 if end > d.textEnd {
269 end = d.textEnd
270 }
271 code := d.text[:end-d.textStart]
272 lookup := d.lookup
273 for pc := start; pc < end; {
274 i := pc - d.textStart
275 text, size := d.disasm(code[i:], pc, lookup, d.byteOrder)
276 file, line, _ := d.pcln.PCToLine(pc)
277 sep := "\t"
278 for len(relocs) > 0 && relocs[0].Addr < i+uint64(size) {
279 text += sep + relocs[0].Stringer.String(pc-start)
280 sep = " "
281 relocs = relocs[1:]
282 }
283 f(pc, uint64(size), file, line, text)
284 pc += uint64(size)
285 }
286 }
287
288 type lookupFunc = func(addr uint64) (sym string, base uint64)
289 type disasmFunc func(code []byte, pc uint64, lookup lookupFunc, ord binary.ByteOrder) (text string, size int)
290
291 func disasm_386(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder) (string, int) {
292 return disasm_x86(code, pc, lookup, 32)
293 }
294
295 func disasm_amd64(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder) (string, int) {
296 return disasm_x86(code, pc, lookup, 64)
297 }
298
299 func disasm_x86(code []byte, pc uint64, lookup lookupFunc, arch int) (string, int) {
300 inst, err := x86asm.Decode(code, arch)
301 var text string
302 size := inst.Len
303 if err != nil || size == 0 || inst.Op == 0 {
304 size = 1
305 text = "?"
306 } else {
307 text = x86asm.GoSyntax(inst, pc, lookup)
308 }
309 return text, size
310 }
311
312 type textReader struct {
313 code []byte
314 pc uint64
315 }
316
317 func (r textReader) ReadAt(data []byte, off int64) (n int, err error) {
318 if off < 0 || uint64(off) < r.pc {
319 return 0, io.EOF
320 }
321 d := uint64(off) - r.pc
322 if d >= uint64(len(r.code)) {
323 return 0, io.EOF
324 }
325 n = copy(data, r.code[d:])
326 if n < len(data) {
327 err = io.ErrUnexpectedEOF
328 }
329 return
330 }
331
332 func disasm_arm(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder) (string, int) {
333 inst, err := armasm.Decode(code, armasm.ModeARM)
334 var text string
335 size := inst.Len
336 if err != nil || size == 0 || inst.Op == 0 {
337 size = 4
338 text = "?"
339 } else {
340 text = armasm.GoSyntax(inst, pc, lookup, textReader{code, pc})
341 }
342 return text, size
343 }
344
345 func disasm_arm64(code []byte, pc uint64, lookup lookupFunc, byteOrder binary.ByteOrder) (string, int) {
346 inst, err := arm64asm.Decode(code)
347 var text string
348 if err != nil || inst.Op == 0 {
349 text = "?"
350 } else {
351 text = arm64asm.GoSyntax(inst, pc, lookup, textReader{code, pc})
352 }
353 return text, 4
354 }
355
356 func disasm_ppc64(code []byte, pc uint64, lookup lookupFunc, byteOrder binary.ByteOrder) (string, int) {
357 inst, err := ppc64asm.Decode(code, byteOrder)
358 var text string
359 size := inst.Len
360 if err != nil || size == 0 {
361 size = 4
362 text = "?"
363 } else {
364 text = ppc64asm.GoSyntax(inst, pc, lookup)
365 }
366 return text, size
367 }
368
369 var disasms = map[string]disasmFunc{
370 "386": disasm_386,
371 "amd64": disasm_amd64,
372 "amd64p32": disasm_amd64,
373 "arm": disasm_arm,
374 "arm64": disasm_arm64,
375 "ppc64": disasm_ppc64,
376 "ppc64le": disasm_ppc64,
377 }
378
379 var byteOrders = map[string]binary.ByteOrder{
380 "386": binary.LittleEndian,
381 "amd64": binary.LittleEndian,
382 "amd64p32": binary.LittleEndian,
383 "arm": binary.LittleEndian,
384 "arm64": binary.LittleEndian,
385 "ppc64": binary.BigEndian,
386 "ppc64le": binary.LittleEndian,
387 "s390x": binary.BigEndian,
388 }
389
390 type Liner interface {
391
392
393 PCToLine(uint64) (string, int, *gosym.Func)
394 }
395
View as plain text