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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gemrelease +2 -0
  3. data/.gitignore +16 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.rake +8 -0
  6. data/Gemfile.rake.lock +51 -0
  7. data/LICENSE.txt +373 -0
  8. data/Makefile +6 -0
  9. data/README.md +43 -0
  10. data/Rakefile +128 -0
  11. data/bin/gdb +2 -0
  12. data/data/tables/README.md +19 -0
  13. data/data/tables/x64.csv +1684 -0
  14. data/data/templates/evoasm-x64.c.erb +319 -0
  15. data/data/templates/evoasm-x64.h.erb +126 -0
  16. data/evoasm.gemspec +30 -0
  17. data/examples/abs.yml +20 -0
  18. data/examples/popcnt.yml +17 -0
  19. data/examples/sym_reg.yml +26 -0
  20. data/exe/evoasm-search +13 -0
  21. data/ext/evoasm_ext/evoasm-alloc.c +145 -0
  22. data/ext/evoasm_ext/evoasm-alloc.h +59 -0
  23. data/ext/evoasm_ext/evoasm-arch.c +44 -0
  24. data/ext/evoasm_ext/evoasm-arch.h +161 -0
  25. data/ext/evoasm_ext/evoasm-bitmap.h +114 -0
  26. data/ext/evoasm_ext/evoasm-buf.c +130 -0
  27. data/ext/evoasm_ext/evoasm-buf.h +47 -0
  28. data/ext/evoasm_ext/evoasm-error.c +31 -0
  29. data/ext/evoasm_ext/evoasm-error.h +75 -0
  30. data/ext/evoasm_ext/evoasm-free-list.c.tmpl +121 -0
  31. data/ext/evoasm_ext/evoasm-free-list.h.tmpl +86 -0
  32. data/ext/evoasm_ext/evoasm-log.c +108 -0
  33. data/ext/evoasm_ext/evoasm-log.h +69 -0
  34. data/ext/evoasm_ext/evoasm-misc.c +23 -0
  35. data/ext/evoasm_ext/evoasm-misc.h +282 -0
  36. data/ext/evoasm_ext/evoasm-param.h +37 -0
  37. data/ext/evoasm_ext/evoasm-search.c +2145 -0
  38. data/ext/evoasm_ext/evoasm-search.h +214 -0
  39. data/ext/evoasm_ext/evoasm-util.h +40 -0
  40. data/ext/evoasm_ext/evoasm-x64.c +275624 -0
  41. data/ext/evoasm_ext/evoasm-x64.h +5436 -0
  42. data/ext/evoasm_ext/evoasm.c +7 -0
  43. data/ext/evoasm_ext/evoasm.h +23 -0
  44. data/ext/evoasm_ext/evoasm_ext.c +1757 -0
  45. data/ext/evoasm_ext/extconf.rb +31 -0
  46. data/lib/evoasm/cli/search.rb +127 -0
  47. data/lib/evoasm/cli.rb +6 -0
  48. data/lib/evoasm/core_ext/array.rb +9 -0
  49. data/lib/evoasm/core_ext/integer.rb +10 -0
  50. data/lib/evoasm/core_ext/kwstruct.rb +13 -0
  51. data/lib/evoasm/core_ext/range.rb +5 -0
  52. data/lib/evoasm/core_ext.rb +1 -0
  53. data/lib/evoasm/error.rb +20 -0
  54. data/lib/evoasm/examples.rb +27 -0
  55. data/lib/evoasm/gen/enum.rb +169 -0
  56. data/lib/evoasm/gen/name_util.rb +80 -0
  57. data/lib/evoasm/gen/state.rb +176 -0
  58. data/lib/evoasm/gen/state_dsl.rb +152 -0
  59. data/lib/evoasm/gen/strio.rb +27 -0
  60. data/lib/evoasm/gen/translator.rb +1102 -0
  61. data/lib/evoasm/gen/version.rb +5 -0
  62. data/lib/evoasm/gen/x64/funcs.rb +495 -0
  63. data/lib/evoasm/gen/x64/inst.rb +781 -0
  64. data/lib/evoasm/gen/x64.rb +237 -0
  65. data/lib/evoasm/gen.rb +8 -0
  66. data/lib/evoasm/program.rb +23 -0
  67. data/lib/evoasm/search.rb +40 -0
  68. data/lib/evoasm/tasks/gen_task.rb +86 -0
  69. data/lib/evoasm/tasks/template_task.rb +52 -0
  70. data/lib/evoasm/version.rb +3 -0
  71. data/lib/evoasm.rb +22 -0
  72. data/test/test_helper.rb +1 -0
  73. data/test/x64/test_helper.rb +19 -0
  74. data/test/x64/x64_test.rb +87 -0
  75. 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