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  }