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