github.com/decomp/exp@v0.0.0-20210624183419-6d058f5e1da6/disasm/x86/inst.go (about) 1 package x86 2 3 import ( 4 "fmt" 5 "sort" 6 7 "github.com/decomp/exp/bin" 8 "golang.org/x/arch/x86/x86asm" 9 ) 10 11 // String returns the string representation of the instruction. 12 func (inst *Inst) String() string { 13 if inst.IsDummyTerm() { 14 return fmt.Sprintf("; fallthrough %v", inst.Addr) 15 } 16 return inst.Inst.String() 17 } 18 19 // isTerm reports whether the given instruction is a terminating instruction. 20 func (term *Inst) isTerm() bool { 21 switch term.Op { 22 // Loop terminators. 23 case x86asm.LOOP, x86asm.LOOPE, x86asm.LOOPNE: 24 return true 25 // Conditional jump terminators. 26 case x86asm.JA, x86asm.JAE, x86asm.JB, x86asm.JBE, x86asm.JCXZ, x86asm.JE, x86asm.JECXZ, x86asm.JG, x86asm.JGE, x86asm.JL, x86asm.JLE, x86asm.JNE, x86asm.JNO, x86asm.JNP, x86asm.JNS, x86asm.JO, x86asm.JP, x86asm.JRCXZ, x86asm.JS: 27 return true 28 // Unconditional jump terminators. 29 case x86asm.JMP: 30 return true 31 // Return terminators. 32 case x86asm.RET: 33 return true 34 } 35 return false 36 } 37 38 // IsDummyTerm reports whether the given instruction is a dummy terminating 39 // instruction. Dummy terminators are used when a basic block is missing a 40 // terminator and falls through into the succeeding basic block, the address of 41 // which is denoted by term.Addr. 42 func (term *Inst) IsDummyTerm() bool { 43 zero := x86asm.Inst{} 44 return term.Inst == zero 45 } 46 47 // Targets returns the targets of the given terminator instruction. Entry 48 // denotes the entry address of the function containing the terminator 49 // instruction. 50 func (dis *Disasm) Targets(term *Inst, funcEntry bin.Address) []bin.Address { 51 if term.IsDummyTerm() { 52 // Dummy terminator; fall through into the succeeding basic block, the 53 // address of which is denoted by term.Addr. 54 return []bin.Address{term.Addr} 55 } 56 next := term.Addr + bin.Address(term.Len) 57 switch term.Op { 58 // Loop terminators. 59 case x86asm.LOOP, x86asm.LOOPE, x86asm.LOOPNE: 60 targets := dis.Addrs(term.Args[0], term.Addr, next) 61 return append(targets, next) 62 // Conditional jump terminators. 63 case x86asm.JA, x86asm.JAE, x86asm.JB, x86asm.JBE, x86asm.JCXZ, x86asm.JE, x86asm.JECXZ, x86asm.JG, x86asm.JGE, x86asm.JL, x86asm.JLE, x86asm.JNE, x86asm.JNO, x86asm.JNP, x86asm.JNS, x86asm.JO, x86asm.JP, x86asm.JRCXZ, x86asm.JS: 64 targets := dis.Addrs(term.Args[0], term.Addr, next) 65 return append(targets, next) 66 // Unconditional jump terminators. 67 case x86asm.JMP: 68 preTargets := dis.Addrs(term.Args[0], term.Addr, next) 69 var targets []bin.Address 70 for _, target := range preTargets { 71 if dis.isTailCall(funcEntry, target) { 72 dbg.Printf("tail call at %v", term.Addr) 73 } else { 74 // Append target if not part of a tail call. 75 targets = append(targets, target) 76 } 77 } 78 return targets 79 // Return terminators. 80 case x86asm.RET: 81 // no targets. 82 return nil 83 } 84 panic(fmt.Errorf("support for terminator instruction %v not yet implemented", term.Op)) 85 } 86 87 // isTailCall reports whether the given JMP instruction is a tail call 88 // instruction. 89 func (dis *Disasm) isTailCall(funcEntry bin.Address, target bin.Address) bool { 90 funcEnd := dis.funcEnd(funcEntry) 91 if funcEntry <= target && target < funcEnd { 92 // Target inside function body. 93 return false 94 } 95 if parents, ok := dis.Chunks[target]; ok { 96 if parents[funcEntry] { 97 // Target part of function chunk. 98 return false 99 } 100 } 101 if !dis.IsFunc(target) { 102 panic(fmt.Errorf("tail call to non-function address %v from function at %v.\n\ttip: %v may be a function chunk of the parent function at %v\n\tadd to lst: FUNCTION CHUNK AT .text:%08X\n\tadd to json: %q: %q,", target, funcEntry, target, funcEntry, uint64(target), target, funcEntry)) 103 } 104 // Target is a tail call. 105 return true 106 } 107 108 // funcEnd returns the end address of the function, under the assumption that 109 // the function is continuous. 110 func (dis *Disasm) funcEnd(funcEntry bin.Address) bin.Address { 111 less := func(i int) bool { 112 return funcEntry < dis.FuncAddrs[i] 113 } 114 index := sort.Search(len(dis.FuncAddrs), less) 115 if 0 <= index && index < len(dis.FuncAddrs) { 116 return dis.FuncAddrs[index] 117 } 118 return dis.codeEnd() 119 }