evoasm 0.0.2.pre7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gemrelease +2 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/Gemfile.rake +8 -0
- data/Gemfile.rake.lock +51 -0
- data/LICENSE.txt +373 -0
- data/Makefile +6 -0
- data/README.md +43 -0
- data/Rakefile +128 -0
- data/bin/gdb +2 -0
- data/data/tables/README.md +19 -0
- data/data/tables/x64.csv +1684 -0
- data/data/templates/evoasm-x64.c.erb +319 -0
- data/data/templates/evoasm-x64.h.erb +126 -0
- data/evoasm.gemspec +30 -0
- data/examples/abs.yml +20 -0
- data/examples/popcnt.yml +17 -0
- data/examples/sym_reg.yml +26 -0
- data/exe/evoasm-search +13 -0
- data/ext/evoasm_ext/evoasm-alloc.c +145 -0
- data/ext/evoasm_ext/evoasm-alloc.h +59 -0
- data/ext/evoasm_ext/evoasm-arch.c +44 -0
- data/ext/evoasm_ext/evoasm-arch.h +161 -0
- data/ext/evoasm_ext/evoasm-bitmap.h +114 -0
- data/ext/evoasm_ext/evoasm-buf.c +130 -0
- data/ext/evoasm_ext/evoasm-buf.h +47 -0
- data/ext/evoasm_ext/evoasm-error.c +31 -0
- data/ext/evoasm_ext/evoasm-error.h +75 -0
- data/ext/evoasm_ext/evoasm-free-list.c.tmpl +121 -0
- data/ext/evoasm_ext/evoasm-free-list.h.tmpl +86 -0
- data/ext/evoasm_ext/evoasm-log.c +108 -0
- data/ext/evoasm_ext/evoasm-log.h +69 -0
- data/ext/evoasm_ext/evoasm-misc.c +23 -0
- data/ext/evoasm_ext/evoasm-misc.h +282 -0
- data/ext/evoasm_ext/evoasm-param.h +37 -0
- data/ext/evoasm_ext/evoasm-search.c +2145 -0
- data/ext/evoasm_ext/evoasm-search.h +214 -0
- data/ext/evoasm_ext/evoasm-util.h +40 -0
- data/ext/evoasm_ext/evoasm-x64.c +275624 -0
- data/ext/evoasm_ext/evoasm-x64.h +5436 -0
- data/ext/evoasm_ext/evoasm.c +7 -0
- data/ext/evoasm_ext/evoasm.h +23 -0
- data/ext/evoasm_ext/evoasm_ext.c +1757 -0
- data/ext/evoasm_ext/extconf.rb +31 -0
- data/lib/evoasm/cli/search.rb +127 -0
- data/lib/evoasm/cli.rb +6 -0
- data/lib/evoasm/core_ext/array.rb +9 -0
- data/lib/evoasm/core_ext/integer.rb +10 -0
- data/lib/evoasm/core_ext/kwstruct.rb +13 -0
- data/lib/evoasm/core_ext/range.rb +5 -0
- data/lib/evoasm/core_ext.rb +1 -0
- data/lib/evoasm/error.rb +20 -0
- data/lib/evoasm/examples.rb +27 -0
- data/lib/evoasm/gen/enum.rb +169 -0
- data/lib/evoasm/gen/name_util.rb +80 -0
- data/lib/evoasm/gen/state.rb +176 -0
- data/lib/evoasm/gen/state_dsl.rb +152 -0
- data/lib/evoasm/gen/strio.rb +27 -0
- data/lib/evoasm/gen/translator.rb +1102 -0
- data/lib/evoasm/gen/version.rb +5 -0
- data/lib/evoasm/gen/x64/funcs.rb +495 -0
- data/lib/evoasm/gen/x64/inst.rb +781 -0
- data/lib/evoasm/gen/x64.rb +237 -0
- data/lib/evoasm/gen.rb +8 -0
- data/lib/evoasm/program.rb +23 -0
- data/lib/evoasm/search.rb +40 -0
- data/lib/evoasm/tasks/gen_task.rb +86 -0
- data/lib/evoasm/tasks/template_task.rb +52 -0
- data/lib/evoasm/version.rb +3 -0
- data/lib/evoasm.rb +22 -0
- data/test/test_helper.rb +1 -0
- data/test/x64/test_helper.rb +19 -0
- data/test/x64/x64_test.rb +87 -0
- metadata +221 -0
@@ -0,0 +1,781 @@
|
|
1
|
+
require 'evoasm/gen/state_dsl'
|
2
|
+
require 'evoasm/gen/x64/funcs'
|
3
|
+
require 'evoasm/core_ext/array'
|
4
|
+
require 'evoasm/core_ext/integer'
|
5
|
+
|
6
|
+
module Evoasm
|
7
|
+
module Gen
|
8
|
+
module X64
|
9
|
+
Inst = Struct.new :mnem, :opcode,
|
10
|
+
:operands,
|
11
|
+
:encoding, :features,
|
12
|
+
:prefs, :name, :index,
|
13
|
+
:flags, :exceptions do
|
14
|
+
COL_OPCODE = 0
|
15
|
+
COL_MNEM = 1
|
16
|
+
COL_OP_ENC = 2
|
17
|
+
COL_OPS = 3
|
18
|
+
COL_PREFS = 4
|
19
|
+
COL_FEATURES = 5
|
20
|
+
COL_EXCEPTIONS = 6
|
21
|
+
|
22
|
+
HEX_BYTE_REGEXP = /^[A-F0-9]{2}$/
|
23
|
+
|
24
|
+
IMM_OP_REGEXP = /^(imm|rel)(\d+)?$/
|
25
|
+
MEM_OP_REGEXP = /^m(\d*)$/
|
26
|
+
MOFFS_OP_REGEXP = /^moffs(\d+)$/
|
27
|
+
VSIB_OP_REGEXP = /^vm(\d+)(?:x|y)$/
|
28
|
+
RM_OP_REGEXP = %r{^(r\d*|xmm|ymm|zmm|mm)?/m(\d+)$}
|
29
|
+
REG_OP_REGEXP = /^(r|xmm|ymm|zmm|mm)(8|16|32|64)?$/
|
30
|
+
|
31
|
+
Operand = Struct.new :name, :param, :type, :size, :access,
|
32
|
+
:encoded, :mnem, :reg, :implicit,
|
33
|
+
:reg_type, :accessed_bits do
|
34
|
+
alias_method :encoded?, :encoded
|
35
|
+
alias_method :mnem?, :mnem
|
36
|
+
alias_method :implicit?, :implicit
|
37
|
+
end
|
38
|
+
|
39
|
+
include Evoasm::Gen::StateDSL
|
40
|
+
|
41
|
+
private def xmm_regs(zmm: false)
|
42
|
+
regs = X64::REGISTERS.fetch(:xmm).dup
|
43
|
+
regs.concat X64::REGISTERS.fetch(:zmm) if zmm
|
44
|
+
|
45
|
+
regs
|
46
|
+
end
|
47
|
+
|
48
|
+
# NOTE: enum domains need to be sorted
|
49
|
+
# (i.e. by their corresponding C enum numberic value)
|
50
|
+
GP_REGISTERS = X64::REGISTERS.fetch(:gp)[0..-5] - [:SP]
|
51
|
+
def param_domain(param_name)
|
52
|
+
domain =
|
53
|
+
case param_name
|
54
|
+
when :rex_b, :rex_r, :rex_x, :rex_w,
|
55
|
+
:vex_l, :force_rex?, :lock?, :force_sib?,
|
56
|
+
:force_disp32?, :force_long_vex?
|
57
|
+
(0..1)
|
58
|
+
when :address_size
|
59
|
+
[16, 32, 64]
|
60
|
+
when :disp_size
|
61
|
+
[16, 32]
|
62
|
+
when :scale
|
63
|
+
[1, 2, 4, 8]
|
64
|
+
when :modrm_reg
|
65
|
+
(0..7)
|
66
|
+
when :vex_v
|
67
|
+
(0..15)
|
68
|
+
when :reg_base
|
69
|
+
GP_REGISTERS
|
70
|
+
when :reg_index
|
71
|
+
case reg_operands[1].type
|
72
|
+
when :vsib
|
73
|
+
X64::REGISTERS.fetch :xmm
|
74
|
+
when :mem, :rm
|
75
|
+
GP_REGISTERS
|
76
|
+
else
|
77
|
+
fail
|
78
|
+
end
|
79
|
+
when :imm0, :imm1, :imm, :moffs, :rel
|
80
|
+
imm_op = encoded_operands.find {|op| op.param == param_name}
|
81
|
+
case imm_op.size
|
82
|
+
when 8
|
83
|
+
(:INT8_MIN..:INT8_MAX)
|
84
|
+
when 16
|
85
|
+
(:INT16_MIN..:INT16_MAX)
|
86
|
+
when 32
|
87
|
+
(:INT32_MIN..:INT32_MAX)
|
88
|
+
when 64
|
89
|
+
(:INT64_MIN..:INT64_MAX)
|
90
|
+
else
|
91
|
+
fail "unexpected imm size '#{imm_size}' (#{imm_ops})"
|
92
|
+
end
|
93
|
+
when :disp
|
94
|
+
(:INT32_MIN..:INT32_MAX)
|
95
|
+
when :reg0, :reg1, :reg2, :reg3
|
96
|
+
reg_op = encoded_operands.find { |op| op.param == param_name }
|
97
|
+
|
98
|
+
case reg_op.reg_type
|
99
|
+
when :xmm
|
100
|
+
xmm_regs zmm: false
|
101
|
+
when :zmm
|
102
|
+
xmm_regs zmm: true
|
103
|
+
when :gp
|
104
|
+
GP_REGISTERS
|
105
|
+
else
|
106
|
+
X64::REGISTERS.fetch reg_op.reg_type
|
107
|
+
end
|
108
|
+
else
|
109
|
+
fail "missing domain for param #{param_name}"
|
110
|
+
end
|
111
|
+
|
112
|
+
domain
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.load(rows)
|
116
|
+
insts = rows.map.with_index do |row, index|
|
117
|
+
X64::Inst.new(index, row)
|
118
|
+
end
|
119
|
+
|
120
|
+
# make sure name is unique
|
121
|
+
insts.group_by(&:name).each do |name, group|
|
122
|
+
if group.size > 1
|
123
|
+
group.each_with_index do |inst, index|
|
124
|
+
inst.name << "_#{index}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
insts
|
130
|
+
end
|
131
|
+
|
132
|
+
def initialize(index, row)
|
133
|
+
self.index = index
|
134
|
+
self.mnem = row[COL_MNEM]
|
135
|
+
self.encoding = row[COL_OP_ENC]
|
136
|
+
self.opcode = row[COL_OPCODE].split(/\s+/)
|
137
|
+
|
138
|
+
load_features row
|
139
|
+
load_exceptions row
|
140
|
+
load_operands row
|
141
|
+
load_prefs row
|
142
|
+
|
143
|
+
load_flags
|
144
|
+
|
145
|
+
self.name = inst_name
|
146
|
+
end
|
147
|
+
|
148
|
+
def inst_name
|
149
|
+
ops_str = operands.select(&:mnem?).map do |op|
|
150
|
+
op.name.gsub('/m', 'm').downcase
|
151
|
+
end.join('_')
|
152
|
+
|
153
|
+
name = mnem.downcase.tr('/', '_')
|
154
|
+
name << "_#{ops_str}" unless ops_str.empty?
|
155
|
+
name
|
156
|
+
end
|
157
|
+
|
158
|
+
private def load_features(row)
|
159
|
+
self.features = row[COL_FEATURES].strip
|
160
|
+
.tr('/', '_')
|
161
|
+
.split('+')
|
162
|
+
.delete_if(&:empty?)
|
163
|
+
.map { |f| "#{f.downcase}".to_sym }
|
164
|
+
.uniq
|
165
|
+
end
|
166
|
+
|
167
|
+
private def load_exceptions(row)
|
168
|
+
exceptions = row[COL_EXCEPTIONS]
|
169
|
+
|
170
|
+
self.exceptions =
|
171
|
+
if exceptions.nil?
|
172
|
+
[]
|
173
|
+
else
|
174
|
+
exceptions.strip
|
175
|
+
.split('; ')
|
176
|
+
.map { |f| "#{f.downcase}".to_sym }
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
private def load_prefs(row)
|
181
|
+
self.prefs =
|
182
|
+
row[COL_PREFS].split('; ').map do |op|
|
183
|
+
op =~ %r{(.+?):(.+?)/(.+)} or fail("invalid prefix op '#{op}'")
|
184
|
+
value =
|
185
|
+
begin
|
186
|
+
Integer($3)
|
187
|
+
rescue
|
188
|
+
$3.to_sym
|
189
|
+
end
|
190
|
+
|
191
|
+
[$1.to_sym, [$2.to_sym, value]]
|
192
|
+
end.to_h
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.max_reg_params
|
196
|
+
4
|
197
|
+
end
|
198
|
+
|
199
|
+
def reg_param_operands
|
200
|
+
operands.select do |op|
|
201
|
+
(op.type == :reg || op.type == :rm) && op.param
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
private def accessable(type, reg_types = [])
|
207
|
+
operands.each_with_object({}) do |op, hash|
|
208
|
+
params_or_regs = Array(op.send(type))
|
209
|
+
|
210
|
+
next unless (op.type == :reg || op.type == :rm) &&
|
211
|
+
!params_or_regs.empty?
|
212
|
+
|
213
|
+
next unless reg_types.include? op.reg_type
|
214
|
+
|
215
|
+
params_or_regs.each do |param_or_reg|
|
216
|
+
hash[param_or_reg] = op.access
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
IGNORED_OPERAND_NAMES = X64::IGNORED_RFLAGS + X64::IGNORED_MXCSR
|
222
|
+
def load_operands(row)
|
223
|
+
ops = row[COL_OPS].split('; ').map do |op|
|
224
|
+
op =~ /(.*?):([a-z]+(?:\[\d+\.\.\d+\])?)/ || fail
|
225
|
+
[$1, $2]
|
226
|
+
end
|
227
|
+
|
228
|
+
imm_counter = 0
|
229
|
+
reg_counter = 0
|
230
|
+
|
231
|
+
self.operands = []
|
232
|
+
|
233
|
+
ops.each do |op_name, flags|
|
234
|
+
next if IGNORED_OPERAND_NAMES.include? op_name.to_sym
|
235
|
+
|
236
|
+
if op_name.upcase == op_name
|
237
|
+
add_implicit_operand op_name, flags
|
238
|
+
else
|
239
|
+
reg_counter, imm_counter = add_operand op_name, flags, reg_counter, imm_counter
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
private def build_operand(op_name, flags)
|
245
|
+
Operand.new.tap do |operand|
|
246
|
+
operand.name = op_name
|
247
|
+
operand.access = flags.gsub(/[^crwu]/, '').each_char.map(&:to_sym)
|
248
|
+
operand.accessed_bits = {}
|
249
|
+
flags.scan(/([crwu])\[(\d+)\.\.(\d+)\]/) do |acc, from, to|
|
250
|
+
operand.accessed_bits[acc.to_sym] = (from.to_i..to.to_i)
|
251
|
+
end
|
252
|
+
operand.encoded = flags.include? 'e'
|
253
|
+
# mnem operand
|
254
|
+
operand.mnem = flags.include? 'm'
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
private def add_operand(op_name, flags, reg_counter, imm_counter)
|
259
|
+
operand = build_operand op_name, flags
|
260
|
+
|
261
|
+
case op_name
|
262
|
+
when IMM_OP_REGEXP
|
263
|
+
operand.type = :imm
|
264
|
+
operand.size = $2 && $2.to_i
|
265
|
+
|
266
|
+
if $1 == 'imm'
|
267
|
+
operand.param = :"imm#{imm_counter}"
|
268
|
+
imm_counter += 1
|
269
|
+
else
|
270
|
+
operand.param = $1.to_sym
|
271
|
+
end
|
272
|
+
when RM_OP_REGEXP
|
273
|
+
operand.type = :rm
|
274
|
+
operand.reg_type, operand.size = reg_type_and_size($1, $2)
|
275
|
+
when REG_OP_REGEXP
|
276
|
+
operand.type = :reg
|
277
|
+
operand.reg_type, operand.size = reg_type_and_size($1, $2)
|
278
|
+
when MEM_OP_REGEXP
|
279
|
+
operand.type = :mem
|
280
|
+
operand.size = $1.empty? ? nil : $1.to_i
|
281
|
+
when MOFFS_OP_REGEXP
|
282
|
+
operand.type = :mem
|
283
|
+
operand.size = Integer($1)
|
284
|
+
operand.param = :moffs
|
285
|
+
when VSIB_OP_REGEXP
|
286
|
+
operand.type = :vsib
|
287
|
+
operand.size = $1.to_i
|
288
|
+
else
|
289
|
+
raise "unexpected operand '#{op_name}'"
|
290
|
+
end
|
291
|
+
|
292
|
+
if (operand.type == :rm || operand.type == :reg)
|
293
|
+
operand.param = :"reg#{reg_counter}"
|
294
|
+
reg_counter += 1
|
295
|
+
end
|
296
|
+
|
297
|
+
self.operands << operand
|
298
|
+
|
299
|
+
[reg_counter, imm_counter]
|
300
|
+
end
|
301
|
+
|
302
|
+
ALLOWED_REG_SIZES = [8, 16, 32, 64]
|
303
|
+
private def reg_type_and_size(type_match, size_match)
|
304
|
+
case type_match
|
305
|
+
when 'r'
|
306
|
+
size = Integer(size_match)
|
307
|
+
raise "invalid reg size #{size}" unless ALLOWED_REG_SIZES.include?(size)
|
308
|
+
[:gp, size]
|
309
|
+
when 'r32'
|
310
|
+
[:gp, 32]
|
311
|
+
when 'xmm'
|
312
|
+
[:xmm, 128]
|
313
|
+
when 'ymm'
|
314
|
+
[:xmm, 256]
|
315
|
+
when 'zmm'
|
316
|
+
[:zmm, 512]
|
317
|
+
when 'mm'
|
318
|
+
[:mm, 64]
|
319
|
+
else
|
320
|
+
fail "unexpected reg type '#{type_match}/#{size_match}'"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
private def reg_size(reg_type, match)
|
325
|
+
case reg_type
|
326
|
+
when :xmm
|
327
|
+
128
|
328
|
+
when 'xmm', 'ymm'
|
329
|
+
:xmm
|
330
|
+
when 'zmm'
|
331
|
+
:zmm
|
332
|
+
when 'mm'
|
333
|
+
:mm
|
334
|
+
else
|
335
|
+
fail "unexpected reg type '#{reg_op.match}' (#{reg_op})"
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def load_flags
|
340
|
+
flags = []
|
341
|
+
operands.each do |op|
|
342
|
+
flags << op.reg_type
|
343
|
+
flags << :sp if op.reg == :SP
|
344
|
+
flags << :mem if op.type == :mem
|
345
|
+
end
|
346
|
+
flags.uniq!
|
347
|
+
flags.compact!
|
348
|
+
|
349
|
+
self.flags = flags
|
350
|
+
end
|
351
|
+
|
352
|
+
RFLAGS = X64::REGISTERS.fetch :rflags
|
353
|
+
MXCSR = X64::REGISTERS.fetch :mxcsr
|
354
|
+
|
355
|
+
private def add_implicit_operand(op_name, flags)
|
356
|
+
if op_name == 'FLAGS' || op_name == 'RFLAGS'
|
357
|
+
# NOTE: currently all used flags
|
358
|
+
# fall within the bits of 32-bit FLAGS
|
359
|
+
# i.e. all upper bits of RFLAGS are unused
|
360
|
+
RFLAGS.each do |reg_name|
|
361
|
+
add_implicit_operand(reg_name.to_s, flags)
|
362
|
+
end
|
363
|
+
return
|
364
|
+
end
|
365
|
+
|
366
|
+
if op_name =~ /^\d$/
|
367
|
+
operand = build_operand op_name, flags
|
368
|
+
operand.type = :imm
|
369
|
+
else
|
370
|
+
reg_name = op_name.gsub(/\[|\]/, '')
|
371
|
+
operand = build_operand reg_name, flags
|
372
|
+
operand.type = op_name =~ /^\[/ ? :mem : :reg
|
373
|
+
|
374
|
+
#FIXME: find a way to handle
|
375
|
+
# this: memory expressions involving
|
376
|
+
# multiple registers e.g. [RBX + AL] in XLAT
|
377
|
+
if reg_name =~ /\+/
|
378
|
+
reg_name = reg_name.split(/\s*\+\s*/).first
|
379
|
+
end
|
380
|
+
|
381
|
+
sym_reg = reg_name.to_sym
|
382
|
+
|
383
|
+
if RFLAGS.include?(sym_reg)
|
384
|
+
operand.reg = sym_reg
|
385
|
+
operand.reg_type = :rflags
|
386
|
+
elsif MXCSR.include?(sym_reg)
|
387
|
+
operand.reg = sym_reg
|
388
|
+
operand.reg_type = :mxcsr
|
389
|
+
else
|
390
|
+
operand.reg =
|
391
|
+
case reg_name
|
392
|
+
when 'RAX', 'EAX', 'AX', 'AL'
|
393
|
+
:A
|
394
|
+
when 'RCX', 'ECX', 'CX', 'CL'
|
395
|
+
:C
|
396
|
+
when 'RDX', 'EDX', 'DX'
|
397
|
+
:D
|
398
|
+
when 'RBX', 'EBX'
|
399
|
+
:B
|
400
|
+
when 'RSP', 'SP'
|
401
|
+
:SP
|
402
|
+
when 'RBP', 'BP'
|
403
|
+
:BP
|
404
|
+
when 'RSI', 'ESI', 'SIL', 'SI'
|
405
|
+
:SI
|
406
|
+
when 'RDI', 'EDI', 'DIL', 'DI'
|
407
|
+
:DI
|
408
|
+
when 'RIP'
|
409
|
+
operand.reg_type = :ip
|
410
|
+
:IP
|
411
|
+
when 'XMM0'
|
412
|
+
operand.reg_type = :xmm
|
413
|
+
:XMM0
|
414
|
+
else
|
415
|
+
fail ArgumentError, "unexpected register '#{reg_name}'"
|
416
|
+
end
|
417
|
+
operand.reg_type = :gp
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
operand.implicit = true
|
422
|
+
|
423
|
+
self.operands << operand
|
424
|
+
end
|
425
|
+
|
426
|
+
def vex?
|
427
|
+
opcode[0] =~ /^VEX/
|
428
|
+
end
|
429
|
+
|
430
|
+
MAND_PREF_BYTES = %w(66 F2 F3 F0)
|
431
|
+
def write_byte_str(byte_str)
|
432
|
+
write Integer(byte_str, 16), 8
|
433
|
+
end
|
434
|
+
|
435
|
+
def mand_pref_byte?(byte)
|
436
|
+
MAND_PREF_BYTES.include? byte
|
437
|
+
end
|
438
|
+
|
439
|
+
def encode_mand_pref
|
440
|
+
write_byte_str opcode.shift while mand_pref_byte? opcode.first
|
441
|
+
end
|
442
|
+
|
443
|
+
def opcode_shift(first = nil)
|
444
|
+
opcode.shift if first.nil? || opcode.first == first
|
445
|
+
end
|
446
|
+
|
447
|
+
LEGACY_PREF_BYTES = {
|
448
|
+
cs_bt: 0x2E,
|
449
|
+
ss: 0x36,
|
450
|
+
ds_bnt: 0x3E,
|
451
|
+
es: 0x26,
|
452
|
+
fs: 0x64,
|
453
|
+
gs: 0x65,
|
454
|
+
lock: 0xF0,
|
455
|
+
pref66: 0x66,
|
456
|
+
pref67: 0x67
|
457
|
+
}
|
458
|
+
|
459
|
+
LEGACY_PREF_CONDS = {
|
460
|
+
pref67: [:eq, :address_size, 32]
|
461
|
+
}
|
462
|
+
|
463
|
+
def encode_legacy_prefs(&block)
|
464
|
+
writes = []
|
465
|
+
|
466
|
+
LEGACY_PREF_BYTES.each do |pref, byte|
|
467
|
+
next unless prefs.key? pref
|
468
|
+
needed, = prefs.fetch pref
|
469
|
+
|
470
|
+
cond =
|
471
|
+
case needed
|
472
|
+
when :required
|
473
|
+
true
|
474
|
+
when :optional
|
475
|
+
:"#{pref}?"
|
476
|
+
when :operand
|
477
|
+
LEGACY_PREF_CONDS.fetch pref
|
478
|
+
else
|
479
|
+
fail
|
480
|
+
end
|
481
|
+
|
482
|
+
writes << [cond, [byte, 8]]
|
483
|
+
end
|
484
|
+
|
485
|
+
return block[] if writes.empty?
|
486
|
+
|
487
|
+
unordered_writes :legacy_pref_order, writes
|
488
|
+
to(&block)
|
489
|
+
end
|
490
|
+
|
491
|
+
def encode_opcode
|
492
|
+
write_byte_str opcode.shift while opcode.first =~ HEX_BYTE_REGEXP
|
493
|
+
end
|
494
|
+
|
495
|
+
def encode_o_opcode
|
496
|
+
return unless encoding.include? 'O'
|
497
|
+
opcode.shift =~ /^([[:xdigit:]]{2})\+r(?:b|w|d|q)$/ || fail
|
498
|
+
byte = Integer($1, 16)
|
499
|
+
write [:add, byte, [:mod, [:reg_code, :reg0], 8]], 8
|
500
|
+
reg_op, = reg_operands
|
501
|
+
reg_op.param == :reg0 or fail "expected reg_op to have param reg0 not #{reg_op.param}"
|
502
|
+
access :reg0, reg_op.access
|
503
|
+
end
|
504
|
+
|
505
|
+
def encodes_modrm?
|
506
|
+
encoding.include? 'M'
|
507
|
+
end
|
508
|
+
|
509
|
+
def encode_modrm_sib(&block)
|
510
|
+
return block[] unless encodes_modrm?
|
511
|
+
|
512
|
+
# modrm_reg is the bitstring
|
513
|
+
# that is used directly to set the ModRM.reg bits
|
514
|
+
# and can be an opcode extension
|
515
|
+
# reg_reg is a *register*.
|
516
|
+
# if given instead, it is properly handled and encoded
|
517
|
+
reg_op, rm_op, = reg_operands
|
518
|
+
|
519
|
+
byte = opcode.shift
|
520
|
+
byte =~ %r{/(r|\d|\?)} or fail "unexpected opcode byte #{byte} in #{mnem}"
|
521
|
+
|
522
|
+
reg_reg_param, rm_reg_param, modrm_reg =
|
523
|
+
case $1
|
524
|
+
when 'r'
|
525
|
+
[reg_op.param, rm_op.param, nil]
|
526
|
+
when /^(\d)$/
|
527
|
+
[nil, rm_op.param, Integer($1)]
|
528
|
+
when '?'
|
529
|
+
[nil, rm_op.param, nil]
|
530
|
+
else
|
531
|
+
fail "unexpected modrm reg specifier '#{$1}'"
|
532
|
+
end
|
533
|
+
|
534
|
+
rm_reg_access = rm_op&.access
|
535
|
+
reg_reg_access = reg_op&.access
|
536
|
+
|
537
|
+
rm_type = rm_op.type
|
538
|
+
|
539
|
+
modrm_sib = ModRMSIB.new reg_reg_param: reg_reg_param,
|
540
|
+
rm_reg_param: rm_reg_param,
|
541
|
+
rm_type: rm_type,
|
542
|
+
modrm_reg: modrm_reg,
|
543
|
+
rm_reg_access: rm_reg_access,
|
544
|
+
reg_reg_access: reg_reg_access
|
545
|
+
|
546
|
+
call modrm_sib
|
547
|
+
|
548
|
+
to(&block)
|
549
|
+
end
|
550
|
+
|
551
|
+
def rex_possible?
|
552
|
+
encoding =~ /M|O|R/
|
553
|
+
end
|
554
|
+
|
555
|
+
def encode_rex_or_vex(&block)
|
556
|
+
if vex?
|
557
|
+
encode_vex(&block)
|
558
|
+
elsif rex_possible?
|
559
|
+
encode_rex(&block)
|
560
|
+
else
|
561
|
+
block[]
|
562
|
+
end
|
563
|
+
end
|
564
|
+
|
565
|
+
def encode_vex(&block)
|
566
|
+
vex = opcode.shift.split '.'
|
567
|
+
fail "invalid VEX start '#{vex.first}'" unless vex.first == 'VEX'
|
568
|
+
|
569
|
+
vex_m =
|
570
|
+
if vex.include? '0F38'
|
571
|
+
0b10
|
572
|
+
elsif vex.include? '0F3A'
|
573
|
+
0b11
|
574
|
+
else
|
575
|
+
0b01
|
576
|
+
end
|
577
|
+
|
578
|
+
vex_p =
|
579
|
+
if vex.include? '66'
|
580
|
+
0b01
|
581
|
+
elsif vex.include? 'F3'
|
582
|
+
0b10
|
583
|
+
elsif vex.include? 'F2'
|
584
|
+
0b11
|
585
|
+
else
|
586
|
+
0b00
|
587
|
+
end
|
588
|
+
|
589
|
+
# vex_type = vex.&(%w(NDS NDD DDS)).first
|
590
|
+
|
591
|
+
rex_w =
|
592
|
+
if vex.include? 'W1'
|
593
|
+
0b01
|
594
|
+
elsif vex.include? 'W0'
|
595
|
+
0b00
|
596
|
+
end
|
597
|
+
|
598
|
+
reg_op, rm_op, vex_op = reg_operands
|
599
|
+
|
600
|
+
if vex_op
|
601
|
+
access vex_op.param, vex_op.access
|
602
|
+
end
|
603
|
+
|
604
|
+
vex_v =
|
605
|
+
case encoding
|
606
|
+
when 'RVM', 'RVMI', 'RVMR', 'MVR', 'RMV', 'RMVI', 'VM', 'VMI'
|
607
|
+
[:reg_code, vex_op.param]
|
608
|
+
when 'RM', 'RMI', 'XM', 'MR', 'MRI', 'M'
|
609
|
+
0b0000
|
610
|
+
when 'NP'
|
611
|
+
nil
|
612
|
+
else
|
613
|
+
fail "unknown VEX encoding #{encoding} in #{mnem}"
|
614
|
+
end
|
615
|
+
|
616
|
+
vex_l =
|
617
|
+
if vex.include? 'LIG'
|
618
|
+
nil
|
619
|
+
else
|
620
|
+
if vex.include? '128'
|
621
|
+
0b0
|
622
|
+
elsif vex.include? '256'
|
623
|
+
0b1
|
624
|
+
elsif vex.include? 'LZ'
|
625
|
+
0b0
|
626
|
+
# [:if, [:eq, [:operand_size], 128], 0b0, 0b1]
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
630
|
+
vex = VEX.new rex_w: rex_w,
|
631
|
+
reg_reg_param: reg_op&.param,
|
632
|
+
rm_reg_param: rm_op&.param,
|
633
|
+
vex_m: vex_m,
|
634
|
+
vex_v: vex_v,
|
635
|
+
vex_l: vex_l,
|
636
|
+
vex_p: vex_p
|
637
|
+
|
638
|
+
call vex
|
639
|
+
to(&block)
|
640
|
+
end
|
641
|
+
|
642
|
+
private def encoded_operands
|
643
|
+
@encoded_operands ||= operands.select(&:encoded?)
|
644
|
+
end
|
645
|
+
|
646
|
+
def encoded_operand_names
|
647
|
+
encoded_operands.map(&:name)
|
648
|
+
end
|
649
|
+
|
650
|
+
private def reg_operands
|
651
|
+
return @regs if @regs
|
652
|
+
|
653
|
+
r_idx = encoding.index(/R|O/)
|
654
|
+
reg_reg = r_idx && encoded_operands[r_idx]
|
655
|
+
|
656
|
+
m_idx = encoding.index 'M'
|
657
|
+
reg_rm = m_idx && encoded_operands[m_idx]
|
658
|
+
|
659
|
+
v_idx = encoding.index 'V'
|
660
|
+
reg_vex = v_idx && encoded_operands[v_idx]
|
661
|
+
|
662
|
+
@regs = [reg_reg, reg_rm, reg_vex]
|
663
|
+
end
|
664
|
+
|
665
|
+
def encode_rex(&block)
|
666
|
+
rex_w_required, rex_w_value = prefs[:rex_w]
|
667
|
+
|
668
|
+
case rex_w_required
|
669
|
+
# 64-bit operand size
|
670
|
+
when :required
|
671
|
+
force_rex = true
|
672
|
+
rex_w = rex_w_value
|
673
|
+
# non 64-bit operand size
|
674
|
+
# only to access extended regs
|
675
|
+
when :optional
|
676
|
+
force_rex = false
|
677
|
+
|
678
|
+
rex_w = case rex_w_value
|
679
|
+
when :any then nil
|
680
|
+
when 0x0 then 0x0
|
681
|
+
else fail "unexpected REX pref value #{rex_w_value}"
|
682
|
+
end
|
683
|
+
else
|
684
|
+
force_rex = false
|
685
|
+
rex_w = nil
|
686
|
+
end
|
687
|
+
|
688
|
+
reg_op, rm_op, _ = reg_operands
|
689
|
+
|
690
|
+
rex = REX.new force: force_rex,
|
691
|
+
rex_w: rex_w,
|
692
|
+
reg_reg_param: reg_op&.param,
|
693
|
+
rm_reg_param: rm_op&.param,
|
694
|
+
rm_reg_type: rm_op&.type,
|
695
|
+
modrm: encodes_modrm?
|
696
|
+
|
697
|
+
call rex
|
698
|
+
to(&block)
|
699
|
+
end
|
700
|
+
|
701
|
+
def imm_param_name
|
702
|
+
case encoding
|
703
|
+
when 'FD', 'TD'
|
704
|
+
:moffs
|
705
|
+
when 'D'
|
706
|
+
:rel
|
707
|
+
else
|
708
|
+
@imm_counter ||= 0
|
709
|
+
:"imm#{@imm_counter}".tap do
|
710
|
+
@imm_counter += 1
|
711
|
+
end
|
712
|
+
end
|
713
|
+
end
|
714
|
+
|
715
|
+
def encode_imm_or_imm_reg
|
716
|
+
while byte = opcode.shift
|
717
|
+
case byte
|
718
|
+
when /^(?:i|c)(?:b|w|d|o|q)$/
|
719
|
+
write imm_param_name, imm_code_size(byte)
|
720
|
+
when '/is4'
|
721
|
+
write [:shl, [:reg_code, :reg3], 4], 8
|
722
|
+
else
|
723
|
+
fail "invalid immediate specifier '#{byte}'"\
|
724
|
+
" found in immediate encoding #{mnem}" if encoding =~ /I$/
|
725
|
+
end
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
def access_implicit_ops
|
730
|
+
operands.each do |op|
|
731
|
+
if op.implicit? && op.type == :reg
|
732
|
+
access op.reg, op.access
|
733
|
+
end
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
state def root_state
|
738
|
+
state do
|
739
|
+
comment mnem
|
740
|
+
log :debug, name
|
741
|
+
|
742
|
+
access_implicit_ops
|
743
|
+
|
744
|
+
encode_legacy_prefs do
|
745
|
+
encode_mand_pref
|
746
|
+
encode_rex_or_vex do
|
747
|
+
encode_opcode
|
748
|
+
encode_o_opcode
|
749
|
+
|
750
|
+
encode_modrm_sib do
|
751
|
+
encode_imm_or_imm_reg
|
752
|
+
done
|
753
|
+
end
|
754
|
+
end
|
755
|
+
end
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
def imm_code_size(code)
|
760
|
+
case code
|
761
|
+
when 'ib', 'cb' then 8
|
762
|
+
when 'iw', 'cw' then 16
|
763
|
+
when 'id', 'cd' then 32
|
764
|
+
when 'io', 'cq' then 64
|
765
|
+
else fail "invalid imm code #{code}"
|
766
|
+
end
|
767
|
+
end
|
768
|
+
|
769
|
+
def done
|
770
|
+
ret
|
771
|
+
end
|
772
|
+
|
773
|
+
end
|
774
|
+
|
775
|
+
class Inst
|
776
|
+
OPERAND_TYPES = %i(reg rm vsib mem imm)
|
777
|
+
end
|
778
|
+
|
779
|
+
end
|
780
|
+
end
|
781
|
+
end
|