github.com/decomp/exp@v0.0.0-20210624183419-6d058f5e1da6/disasm/x86/decode.go (about) 1 package x86 2 3 import ( 4 "sort" 5 6 "github.com/decomp/exp/bin" 7 "github.com/pkg/errors" 8 "golang.org/x/arch/x86/x86asm" 9 ) 10 11 // A Func is a function. 12 type Func struct { 13 // Address of the function. 14 Addr bin.Address 15 // Basic blocks of the function. 16 Blocks map[bin.Address]*BasicBlock 17 } 18 19 // A BasicBlock is a basic block; a sequence of non-branching instructions 20 // terminated by a branching instruction. 21 type BasicBlock struct { 22 // Address of the basic block. 23 Addr bin.Address 24 // Sequence of non-branching instructions. 25 Insts []*Inst 26 // Terminating instruction. 27 Term *Inst 28 } 29 30 // An Inst is a single instruction. 31 type Inst struct { 32 // Address of the instruction. 33 Addr bin.Address 34 // x86 instruction. 35 x86asm.Inst 36 } 37 38 // DecodeFunc decodes and returns the function at the given address. 39 func (dis *Disasm) DecodeFunc(entry bin.Address) (*Func, error) { 40 dbg.Printf("decoding function at %v", entry) 41 f := &Func{ 42 Addr: entry, 43 Blocks: make(map[bin.Address]*BasicBlock), 44 } 45 queue := newQueue() 46 queue.push(entry) 47 for !queue.empty() { 48 blockAddr := queue.pop() 49 if _, ok := f.Blocks[blockAddr]; ok { 50 // skip basic block if already decoded. 51 continue 52 } 53 block, err := dis.DecodeBlock(blockAddr) 54 if err != nil { 55 return nil, errors.WithStack(err) 56 } 57 f.Blocks[blockAddr] = block 58 // Add block targets to queue. 59 targets := dis.Targets(block.Term, entry) 60 for _, target := range targets { 61 dbg.Printf("adding basic block address %v to queue", target) 62 queue.push(target) 63 } 64 } 65 return f, nil 66 } 67 68 // DecodeBlock decodes and returns the basic block at the given address. 69 func (dis *Disasm) DecodeBlock(entry bin.Address) (*BasicBlock, error) { 70 dbg.Printf("decoding basic block at %v", entry) 71 // Compute end address of the basic block. 72 maxLen := dis.maxBlockLen(entry) 73 addr := entry 74 end := entry + bin.Address(maxLen) 75 // Decode instructions. 76 block := &BasicBlock{ 77 Addr: entry, 78 } 79 for addr < end { 80 inst, err := dis.DecodeInst(addr) 81 if err != nil { 82 return nil, errors.WithStack(err) 83 } 84 dbg.Printf(" instruction at %v: %v", addr, inst) 85 addr += bin.Address(inst.Len) 86 if inst.isTerm() { 87 block.Term = inst 88 break 89 } 90 block.Insts = append(block.Insts, inst) 91 } 92 // Sanity check. 93 if addr != end { 94 warn.Printf("unexpected end address of basic block at %v; expected %v, got %v", entry, end, addr) 95 } 96 // Add dummy terminator for fallthrough basic blocks. 97 if block.Term == nil { 98 block.Term = &Inst{ 99 Addr: end, 100 } 101 } 102 return block, nil 103 } 104 105 // DecodeInst decodes and returns the instruction at the given address. 106 func (dis *Disasm) DecodeInst(addr bin.Address) (*Inst, error) { 107 code := dis.File.Code(addr) 108 i, err := x86asm.Decode(code, dis.Mode) 109 if err != nil { 110 return nil, errors.WithStack(err) 111 } 112 inst := &Inst{ 113 Addr: addr, 114 Inst: i, 115 } 116 return inst, nil 117 } 118 119 // maxBlockLen returns the maximum length of the given basic block. 120 func (dis *Disasm) maxBlockLen(blockAddr bin.Address) int64 { 121 less := func(i int) bool { 122 return blockAddr < dis.Frags[i].Addr 123 } 124 index := sort.Search(len(dis.Frags), less) 125 if 0 <= index && index < len(dis.Frags) { 126 return int64(dis.Frags[index].Addr - blockAddr) 127 } 128 return int64(dis.codeEnd() - blockAddr) 129 } 130 131 // codeStart returns the start address of the first code section. 132 func (dis *Disasm) codeStart() bin.Address { 133 var min bin.Address 134 for _, sect := range dis.File.Sections { 135 if sect.Perm&bin.PermX != 0 { 136 start := sect.Addr 137 if min == 0 || min > start { 138 min = start 139 } 140 } 141 } 142 if min == 0 { 143 panic("unable to locate start address of first code section") 144 } 145 return min 146 } 147 148 // codeEnd returns the end address of the last code section. 149 func (dis *Disasm) codeEnd() bin.Address { 150 var max bin.Address 151 for _, sect := range dis.File.Sections { 152 if sect.Perm&bin.PermX != 0 { 153 end := sect.Addr + bin.Address(len(sect.Data)) 154 if max < end { 155 max = end 156 } 157 } 158 } 159 if max == 0 { 160 panic("unable to locate end address of last code section") 161 } 162 return max 163 }