github.com/decomp/exp@v0.0.0-20210624183419-6d058f5e1da6/disasm/disasm.go (about) 1 // Package disasm provides general disassembler primitives. 2 package disasm 3 4 import ( 5 "log" 6 "os" 7 "sort" 8 9 "github.com/decomp/exp/bin" 10 "github.com/mewkiz/pkg/jsonutil" 11 "github.com/mewkiz/pkg/osutil" 12 "github.com/mewkiz/pkg/term" 13 "github.com/pkg/errors" 14 ) 15 16 // TODO: Remove loggers once the library matures. 17 18 // Loggers. 19 var ( 20 // dbg represents a logger with the "disasm:" prefix, which logs debug 21 // messages to standard error. 22 dbg = log.New(os.Stderr, term.BlueBold("disasm:")+" ", 0) 23 // warn represents a logger with the "warning:" prefix, which logs warning 24 // messages to standard error. 25 warn = log.New(os.Stderr, term.RedBold("warning:")+" ", 0) 26 ) 27 28 // A Disasm tracks information required to disassemble a binary executable. 29 // 30 // Data should only be written to this structure during initialization. After 31 // initialization the structure is considered in read-only mode to allow for 32 // concurrent decoding of functions. 33 type Disasm struct { 34 // Binary executable. 35 File *bin.File 36 // Function addresses. 37 FuncAddrs []bin.Address 38 // Basic block addresses. 39 BlockAddrs []bin.Address 40 // Map from jump table address to target addresses. 41 Tables map[bin.Address][]bin.Address 42 // Map from basic block address to function address. The basic block is a 43 // function chunk and part of a discontinuous function. 44 Chunks map[bin.Address]map[bin.Address]bool 45 // Fragments; sequences of bytes. 46 Frags []*Fragment 47 } 48 49 // New creates a new Disasm for accessing the assembly instructions of the given 50 // binary executable, and the information contained within associated JSON and 51 // LLVM IR files. 52 // 53 // Associated files of the generic disassembler. 54 // 55 // funcs.json 56 // blocks.json 57 // tables.json 58 // chunks.json 59 // data.json 60 func New(file *bin.File) (*Disasm, error) { 61 // Prepare generic disassembler. 62 dis := &Disasm{ 63 File: file, 64 Tables: make(map[bin.Address][]bin.Address), 65 Chunks: make(map[bin.Address]map[bin.Address]bool), 66 } 67 68 // Parse function addresses. 69 if err := parseJSON("funcs.json", &dis.FuncAddrs); err != nil { 70 return nil, errors.WithStack(err) 71 } 72 sort.Sort(bin.Addresses(dis.FuncAddrs)) 73 74 // Parse basic block addresses. 75 if err := parseJSON("blocks.json", &dis.BlockAddrs); err != nil { 76 return nil, errors.WithStack(err) 77 } 78 sort.Sort(bin.Addresses(dis.BlockAddrs)) 79 80 // Add entry point function to function and basic block addresses. 81 dis.FuncAddrs = bin.InsertAddr(dis.FuncAddrs, dis.File.Entry) 82 dis.BlockAddrs = bin.InsertAddr(dis.BlockAddrs, dis.File.Entry) 83 84 // Add export functions to function and basic block addresses. 85 for addr := range dis.File.Exports { 86 dis.FuncAddrs = bin.InsertAddr(dis.FuncAddrs, addr) 87 dis.BlockAddrs = bin.InsertAddr(dis.BlockAddrs, addr) 88 } 89 90 // Parse jump table targets. 91 if err := parseJSON("tables.json", &dis.Tables); err != nil { 92 return nil, errors.WithStack(err) 93 } 94 95 // Parse function chunks. 96 if err := parseJSON("chunks.json", &dis.Chunks); err != nil { 97 return nil, errors.WithStack(err) 98 } 99 100 // Compute fragments of the binary; distinct byte sequences of either code or 101 // data. 102 // 103 // Parse data addresses. 104 var dataAddrs []bin.Address 105 if err := parseJSON("data.json", &dataAddrs); err != nil { 106 return nil, errors.WithStack(err) 107 } 108 // Append basic block addresses to fragments. 109 for _, blockAddr := range dis.BlockAddrs { 110 frag := &Fragment{ 111 Addr: blockAddr, 112 Kind: KindCode, 113 } 114 dis.Frags = append(dis.Frags, frag) 115 } 116 // Append data addresses to fragments. 117 for _, dataAddr := range dataAddrs { 118 frag := &Fragment{ 119 Addr: dataAddr, 120 Kind: KindData, 121 } 122 dis.Frags = append(dis.Frags, frag) 123 } 124 // Sort fragments based on address. 125 less := func(i, j int) bool { 126 return dis.Frags[i].Addr < dis.Frags[j].Addr 127 } 128 sort.Slice(dis.Frags, less) 129 130 return dis, nil 131 } 132 133 // IsFunc reports whether the given address is the entry address of a function. 134 func (dis *Disasm) IsFunc(addr bin.Address) bool { 135 less := func(i int) bool { 136 return addr <= dis.FuncAddrs[i] 137 } 138 index := sort.Search(len(dis.FuncAddrs), less) 139 if index < len(dis.FuncAddrs) { 140 return dis.FuncAddrs[index] == addr 141 } 142 if _, ok := dis.File.Imports[addr]; ok { 143 return true 144 } 145 return false 146 } 147 148 // A Fragment represents a sequence of bytes (either code or data). 149 type Fragment struct { 150 // Start address of fragment. 151 Addr bin.Address 152 // Byte sequence type (code or data). 153 Kind FragmentKind 154 } 155 156 // FragmentKind specifies the set of byte sequence types (either code or data). 157 type FragmentKind uint 158 159 // Fragment kinds. 160 const ( 161 // The sequence of bytes contains code. 162 KindCode = iota + 1 163 // The sequence of bytes contains data. 164 KindData 165 ) 166 167 // ### [ Helper functions ] #################################################### 168 169 // parseJSON parses the given JSON file and stores the result into v. 170 func parseJSON(jsonPath string, v interface{}) error { 171 if !osutil.Exists(jsonPath) { 172 warn.Printf("unable to locate JSON file %q", jsonPath) 173 return nil 174 } 175 dbg.Printf("parsing: %q", jsonPath) 176 return jsonutil.ParseFile(jsonPath, v) 177 }