-
Notifications
You must be signed in to change notification settings - Fork 3
/
inst_matcher.go
286 lines (239 loc) · 8.12 KB
/
inst_matcher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
package x64
import (
"fmt"
"github.com/wdamron/x64/feats"
flags "github.com/wdamron/x64/internal/flags"
)
// Placeholder for memory arguments, to avoid allocations when converting Mem to an interface
type memArgPlaceholder struct{}
func (m memArgPlaceholder) isArg() {}
func (m memArgPlaceholder) width() uint8 { return 0 }
// InstMatcher finds valid encodings for an instruction with arguments.
type InstMatcher struct {
// enabled CPU features:
feats feats.Feature
// scratch space for current instruction, arguments, and matched encoding:
addrSize int
opSize int
memOffset int // -1 if no memory argument is present
mem Mem // memory argument if memOffset >= 0
args []Arg // sized reference to _args
_args [4]Arg
inst Inst
encId uint // offset of the matched encoding
enc enc // matched encoding
argp []byte // arg-pattern for the matched encoding
// extracted arguments:
r Arg
m Arg
v Arg
i Arg
imms []Arg
_imms [4]Arg
}
// Create an instruction matcher with all CPU features enabled by default.
func NewInstMatcher() *InstMatcher {
return &InstMatcher{
feats: feats.AllFeatures,
memOffset: -1,
addrSize: -1,
opSize: -1,
}
}
func (m *InstMatcher) reset() {
*m = InstMatcher{feats: m.feats, addrSize: -1, opSize: -1, memOffset: -1}
}
// Get the current, allowable CPU feature-set for instruction-matching.
//
// See package x64/feats for all available CPU features.
func (m *InstMatcher) Features() feats.Feature { return m.feats }
// Restrict the allowable CPU feature-set for instruction-matching.
//
// See package x64/feats for all available CPU features.
func (m *InstMatcher) SetFeatures(enabledFeatures feats.Feature) { m.feats = enabledFeatures }
// Control the allowable CPU feature-set for instruction-matching.
//
// See package x64/feats for all available CPU features.
func (m *InstMatcher) DisableFeature(feature feats.Feature) { m.feats &^= feature }
// Control the allowable CPU feature-set for instruction-matching.
//
// See package x64/feats for all available CPU features.
func (m *InstMatcher) EnableFeature(feature feats.Feature) { m.feats |= feature }
// Get the instruction's unique encoding ID.
func (m *InstMatcher) EncodingId() uint { return m.encId }
// Get CPU features required by the instruction.
func (m *InstMatcher) InstFeatures() feats.Feature { return m.enc.feats }
// Get the instruction's address size.
func (m *InstMatcher) AddrSize() int { return m.addrSize }
// Get the instruction's operand size.
func (m *InstMatcher) OperandSize() int { return m.opSize }
// Get the instruction's opcode
func (m *InstMatcher) Opcode() []byte { return m.enc.op[:m.enc.oplen()] }
// Check if a register argument will be encoded in the last byte of the instruction's opcode.
func (m *InstMatcher) HasOpcodeRegArg() bool { return m.enc.flags&flags.SHORT_ARG != 0 }
// Check if the instruction is part of the VEX instruction set.
func (m *InstMatcher) IsVEX() bool { return m.enc.flags&flags.VEX_OP != 0 }
// Check if the instruction is part of the XOP instruction set.
func (m *InstMatcher) IsXOP() bool { return m.enc.flags&flags.XOP_OP != 0 }
// Check if the instruction encodes the final opcode byte in the immediate position, like 3DNow! ops.
func (m *InstMatcher) HasOpcodeInImmediate() bool { return m.enc.flags&flags.IMM_OP != 0 }
func (m *InstMatcher) Match(inst Inst, args ...Arg) error {
if err := m.prepare(inst, args...); err != nil {
return err
}
return m.match(0)
}
// Find all matching encodings for an instruction. If no matches are found, ErrNoMatch will be returned.
func (m *InstMatcher) AllMatches(inst Inst, args ...Arg) ([]InstMatcher, error) {
var matches []InstMatcher
start := uint16(inst.offset())
count := uint16(inst.count())
offset := uint16(0)
for offset < count {
if err := m.prepare(inst, args...); err != nil {
return nil, err
}
if err := m.match(offset); err == nil {
matches = append(matches, *m)
offset = uint16(m.EncodingId()) + 1 - start
continue
}
offset++
}
m.reset()
if len(matches) == 0 {
return nil, ErrNoMatch
}
return matches, nil
}
func (m *InstMatcher) prepare(inst Inst, args ...Arg) error {
m.reset()
m.inst = inst
for i, arg := range args {
if mem, ok := arg.(Mem); ok {
if m.memOffset >= 0 {
m.reset()
return fmt.Errorf("Multiple memory arguments are not supported")
}
m._args[i] = memArgPlaceholder{}
m.memOffset = i
m.mem = mem
continue
}
m._args[i] = arg
}
m.args = m._args[:len(args)]
return nil
}
func (m *InstMatcher) match(encodingStartOffset uint16) error {
addrSize, err := m.sanitizeMemArg()
if err != nil {
m.reset()
return err
}
if addrSize < 0 {
addrSize = 8
}
if addrSize != 4 && addrSize != 8 {
return fmt.Errorf("Impossible address size for %s: %v", m.inst.Name(), addrSize)
}
// find a matching encoding
if ok := m.matchInst(m.feats, encodingStartOffset); !ok {
m.reset()
return ErrNoMatch
}
opSize, err := m.resizeArgs()
if err != nil {
m.reset()
return err
}
if err = m.extractArgs(); err != nil {
m.reset()
return err
}
m.addrSize, m.opSize = int(addrSize), int(opSize)
return nil
}
// Find an encoding for inst with a register destination and register source.
// If no matching instruction-encoding is found, ErrNoMatch will be returned.
func (m *InstMatcher) RR(inst Inst, dst, src Reg) error {
return m.regRegImm(inst, dst, src, nil)
}
// Find an encoding for inst with a register destination, register source, and immediate.
// If no matching instruction-encoding is found, ErrNoMatch will be returned.
func (m *InstMatcher) RRI(inst Inst, dst, src Reg, imm ImmArg) error {
return m.regRegImm(inst, dst, src, imm)
}
// Find an encoding for inst with a register destination and memory source.
// If no matching instruction-encoding is found, ErrNoMatch will be returned.
func (m *InstMatcher) RM(inst Inst, dst Reg, src Mem) error {
return m.regMemImm(inst, dst, src, nil, false)
}
// Find an encoding for inst with a memory destination and register source.
// If no matching instruction-encoding is found, ErrNoMatch will be returned.
func (m *InstMatcher) MR(inst Inst, dst Mem, src Reg) error {
return m.regMemImm(inst, src, dst, nil, true)
}
// Find an encoding for inst with a register destination, memory source, and immediate.
// If no matching instruction-encoding is found, ErrNoMatch will be returned.
func (m *InstMatcher) RMI(inst Inst, dst Reg, src Mem, imm ImmArg) error {
return m.regMemImm(inst, dst, src, imm, false)
}
// Find an encoding for inst with a memory destination, register source, and immediate.
// If no matching instruction-encoding is found, ErrNoMatch will be returned.
func (m *InstMatcher) MRI(inst Inst, dst Mem, src Reg, imm ImmArg) error {
return m.regMemImm(inst, src, dst, imm, true)
}
// Find an encoding for inst with a register destination and immediate.
// If no matching instruction-encoding is found, ErrNoMatch will be returned.
func (m *InstMatcher) RI(inst Inst, dst Reg, imm ImmArg) error {
m.reset()
m.inst = inst
m._args[0], m._args[1] = dst, imm
if imm != nil {
m.args = m._args[:2]
} else {
m.args = m._args[:1]
}
return m.match(0)
}
// Find an encoding for inst with a memory destination and immediate.
// If no matching instruction-encoding is found, ErrNoMatch will be returned.
func (m *InstMatcher) MI(inst Inst, dst Mem, imm ImmArg) error {
m.reset()
m.inst, m.memOffset, m.mem, m._args[0], m._args[1] = inst, 0, dst, memArgPlaceholder{}, imm
if imm != nil {
m.args = m._args[:2]
} else {
m.args = m._args[:1]
}
return m.match(0)
}
func (m *InstMatcher) regRegImm(inst Inst, dst, src Reg, imm ImmArg) error {
m.reset()
m.inst, m._args[0], m._args[1], m._args[2] = inst, dst, src, imm
if imm != nil {
m.args = m._args[:3]
} else {
m.args = m._args[:2]
}
return m.match(0)
}
func (m *InstMatcher) regMemImm(inst Inst, r Reg, mem Mem, imm ImmArg, swap bool) error {
m.reset()
m.inst, m.mem = inst, mem
if swap {
m.memOffset = 0
m._args[0], m._args[1] = memArgPlaceholder{}, r
} else {
m.memOffset = 1
m._args[0], m._args[1] = r, memArgPlaceholder{}
}
if imm != nil {
m._args[2] = imm
m.args = m._args[:3]
} else {
m.args = m._args[:2]
}
return m.match(0)
}