evoasm 0.0.2.pre7 → 0.1.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gdbinit +41 -0
- data/.gitignore +1 -2
- data/.gitmodules +3 -0
- data/.rubocop.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.md +660 -0
- data/Makefile +1 -1
- data/README.md +17 -9
- data/Rakefile +39 -107
- data/bin/gdb +1 -1
- data/bin/gdb_loop +4 -0
- data/docs/FindingInstructions.md +17 -0
- data/docs/JIT.md +14 -0
- data/docs/SymbolicRegression.md +102 -0
- data/docs/Visualization.md +29 -0
- data/docs/examples/bit_insts.rb +44 -0
- data/docs/examples/jit.rb +26 -0
- data/docs/examples/loss.gif +0 -0
- data/docs/examples/program.png +0 -0
- data/docs/examples/sym_reg.rb +64 -0
- data/docs/examples/vis.rb +38 -0
- data/evoasm.gemspec +21 -15
- data/ext/evoasm_ext/Rakefile +3 -0
- data/ext/evoasm_ext/compile.rake +35 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-alloc.c +226 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-alloc.h +84 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-arch.c +52 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-arch.h +101 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-bitmap.h +158 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-buf.c +204 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-buf.h +109 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-domain.c +124 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-domain.h +279 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-error.c +65 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-error.h +108 -0
- data/ext/evoasm_ext/{evoasm-log.c → libevoasm/src/evoasm-log.c} +36 -18
- data/ext/evoasm_ext/libevoasm/src/evoasm-log.h +93 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-param.c +22 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-param.h +33 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-pop-params.c +192 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-pop-params.h +60 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-pop.c +1323 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-pop.h +107 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-program-io.c +116 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-program-io.h +60 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-program.c +1827 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-program.h +167 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-rand.c +65 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-rand.h +76 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-signal.c +106 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-signal.h +58 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-util.h +112 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-x64.c +925 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm-x64.h +277 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm.c +28 -0
- data/ext/evoasm_ext/libevoasm/src/evoasm.h +35 -0
- data/ext/evoasm_ext/libevoasm/src/gen/evoasm-x64-enums.h +2077 -0
- data/ext/evoasm_ext/libevoasm/src/gen/evoasm-x64-insts.c +191203 -0
- data/ext/evoasm_ext/libevoasm/src/gen/evoasm-x64-insts.h +1713 -0
- data/ext/evoasm_ext/libevoasm/src/gen/evoasm-x64-misc.c +348 -0
- data/ext/evoasm_ext/libevoasm/src/gen/evoasm-x64-misc.h +93 -0
- data/ext/evoasm_ext/libevoasm/src/gen/evoasm-x64-params.c +51 -0
- data/ext/evoasm_ext/libevoasm/src/gen/evoasm-x64-params.h +509 -0
- data/lib/evoasm.rb +28 -11
- data/lib/evoasm/buffer.rb +105 -0
- data/lib/evoasm/capstone.rb +100 -0
- data/lib/evoasm/domain.rb +116 -0
- data/lib/evoasm/error.rb +37 -16
- data/lib/evoasm/exception_error.rb +19 -0
- data/lib/evoasm/ffi_ext.rb +53 -0
- data/lib/evoasm/libevoasm.rb +286 -0
- data/lib/evoasm/libevoasm/x64_enums.rb +1967 -0
- data/lib/evoasm/parameter.rb +20 -0
- data/lib/evoasm/population.rb +145 -0
- data/lib/evoasm/population/parameters.rb +227 -0
- data/lib/evoasm/population/plotter.rb +89 -0
- data/lib/evoasm/prng.rb +64 -0
- data/lib/evoasm/program.rb +195 -12
- data/lib/evoasm/program/io.rb +144 -0
- data/lib/evoasm/test.rb +8 -0
- data/lib/evoasm/version.rb +1 -1
- data/lib/evoasm/x64.rb +115 -0
- data/lib/evoasm/x64/cpu_state.rb +95 -0
- data/lib/evoasm/x64/instruction.rb +109 -0
- data/lib/evoasm/x64/operand.rb +156 -0
- data/lib/evoasm/x64/parameters.rb +211 -0
- data/test/helpers/population_helper.rb +128 -0
- data/test/helpers/test_helper.rb +1 -0
- data/test/helpers/x64_helper.rb +24 -0
- data/test/integration/bitwise_reverse_test.rb +41 -0
- data/test/integration/gcd_test.rb +52 -0
- data/test/integration/popcnt_test.rb +46 -0
- data/test/integration/sym_reg_test.rb +68 -0
- data/test/unit/evoasm/buffer_test.rb +48 -0
- data/test/unit/evoasm/capstone_test.rb +18 -0
- data/test/unit/evoasm/domain_test.rb +55 -0
- data/test/unit/evoasm/population/parameters_test.rb +106 -0
- data/test/unit/evoasm/population_test.rb +96 -0
- data/test/unit/evoasm/prng_test.rb +47 -0
- data/test/unit/evoasm/x64/cpu_state_test.rb +73 -0
- data/test/unit/evoasm/x64/encoding_test.rb +320 -0
- data/test/unit/evoasm/x64/instruction_access_test.rb +177 -0
- data/test/unit/evoasm/x64/instruction_encoding_test.rb +780 -0
- data/test/unit/evoasm/x64/instruction_test.rb +62 -0
- data/test/unit/evoasm/x64/parameters_test.rb +65 -0
- data/test/unit/evoasm/x64_test.rb +52 -0
- metadata +195 -89
- data/Gemfile.rake +0 -8
- data/Gemfile.rake.lock +0 -51
- data/LICENSE.txt +0 -373
- data/data/tables/README.md +0 -19
- data/data/tables/x64.csv +0 -1684
- data/data/templates/evoasm-x64.c.erb +0 -319
- data/data/templates/evoasm-x64.h.erb +0 -126
- data/examples/abs.yml +0 -20
- data/examples/popcnt.yml +0 -17
- data/examples/sym_reg.yml +0 -26
- data/exe/evoasm-search +0 -13
- data/ext/evoasm_ext/evoasm-alloc.c +0 -145
- data/ext/evoasm_ext/evoasm-alloc.h +0 -59
- data/ext/evoasm_ext/evoasm-arch.c +0 -44
- data/ext/evoasm_ext/evoasm-arch.h +0 -161
- data/ext/evoasm_ext/evoasm-bitmap.h +0 -114
- data/ext/evoasm_ext/evoasm-buf.c +0 -130
- data/ext/evoasm_ext/evoasm-buf.h +0 -47
- data/ext/evoasm_ext/evoasm-error.c +0 -31
- data/ext/evoasm_ext/evoasm-error.h +0 -75
- data/ext/evoasm_ext/evoasm-free-list.c.tmpl +0 -121
- data/ext/evoasm_ext/evoasm-free-list.h.tmpl +0 -86
- data/ext/evoasm_ext/evoasm-log.h +0 -69
- data/ext/evoasm_ext/evoasm-misc.c +0 -23
- data/ext/evoasm_ext/evoasm-misc.h +0 -282
- data/ext/evoasm_ext/evoasm-param.h +0 -37
- data/ext/evoasm_ext/evoasm-search.c +0 -2145
- data/ext/evoasm_ext/evoasm-search.h +0 -214
- data/ext/evoasm_ext/evoasm-util.h +0 -40
- data/ext/evoasm_ext/evoasm-x64.c +0 -275624
- data/ext/evoasm_ext/evoasm-x64.h +0 -5436
- data/ext/evoasm_ext/evoasm.c +0 -7
- data/ext/evoasm_ext/evoasm.h +0 -23
- data/ext/evoasm_ext/evoasm_ext.c +0 -1757
- data/ext/evoasm_ext/extconf.rb +0 -31
- data/lib/evoasm/cli.rb +0 -6
- data/lib/evoasm/cli/search.rb +0 -127
- data/lib/evoasm/core_ext.rb +0 -1
- data/lib/evoasm/core_ext/array.rb +0 -9
- data/lib/evoasm/core_ext/integer.rb +0 -10
- data/lib/evoasm/core_ext/kwstruct.rb +0 -13
- data/lib/evoasm/core_ext/range.rb +0 -5
- data/lib/evoasm/examples.rb +0 -27
- data/lib/evoasm/gen.rb +0 -8
- data/lib/evoasm/gen/enum.rb +0 -169
- data/lib/evoasm/gen/name_util.rb +0 -80
- data/lib/evoasm/gen/state.rb +0 -176
- data/lib/evoasm/gen/state_dsl.rb +0 -152
- data/lib/evoasm/gen/strio.rb +0 -27
- data/lib/evoasm/gen/translator.rb +0 -1102
- data/lib/evoasm/gen/version.rb +0 -5
- data/lib/evoasm/gen/x64.rb +0 -237
- data/lib/evoasm/gen/x64/funcs.rb +0 -495
- data/lib/evoasm/gen/x64/inst.rb +0 -781
- data/lib/evoasm/search.rb +0 -40
- data/lib/evoasm/tasks/gen_task.rb +0 -86
- data/lib/evoasm/tasks/template_task.rb +0 -52
- data/test/test_helper.rb +0 -1
- data/test/x64/test_helper.rb +0 -19
- data/test/x64/x64_test.rb +0 -87
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'evoasm/test'
|
2
|
+
require 'evoasm/buffer'
|
3
|
+
require 'evoasm/x64'
|
4
|
+
require 'evoasm/x64/cpu_state'
|
5
|
+
|
6
|
+
require 'x64_helper'
|
7
|
+
|
8
|
+
module Evoasm
|
9
|
+
module X64
|
10
|
+
class InstructionAccessTest < Minitest::Test
|
11
|
+
include X64Helper
|
12
|
+
|
13
|
+
def setup
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.test_order
|
18
|
+
:alpha
|
19
|
+
end
|
20
|
+
|
21
|
+
PARAMETERS = %i(reg0 reg1 reg2 reg3 imm0)
|
22
|
+
|
23
|
+
def random_parameters(instruction)
|
24
|
+
parameters = Evoasm::X64::Parameters.new(basic: true)
|
25
|
+
instruction.parameters.each do |parameter|
|
26
|
+
if PARAMETERS.include? parameter.name
|
27
|
+
parameter_value = parameter.domain.rand
|
28
|
+
parameters[parameter.name] = parameter_value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
parameters
|
33
|
+
end
|
34
|
+
|
35
|
+
def accessed_registers(registers, instruction, parameters, mode)
|
36
|
+
registers.map do |register|
|
37
|
+
word = instruction.operands.find do |operand|
|
38
|
+
case mode
|
39
|
+
when :write
|
40
|
+
next false unless operand.written? || operand.maybe_written?
|
41
|
+
when :read
|
42
|
+
next false unless operand.read?
|
43
|
+
else
|
44
|
+
raise
|
45
|
+
end
|
46
|
+
|
47
|
+
next true if operand.register == register
|
48
|
+
|
49
|
+
parameter_name = operand.parameter&.name
|
50
|
+
next false if parameter_name.nil?
|
51
|
+
parameters[parameter_name] == register
|
52
|
+
end&.word
|
53
|
+
|
54
|
+
[register, word] if word
|
55
|
+
end.compact.to_h
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.define_write_test(instruction)
|
59
|
+
define_method :"test_#{instruction.name}_writes" do
|
60
|
+
buffer = Evoasm::Buffer.new 1024, :mmap
|
61
|
+
|
62
|
+
100.times do
|
63
|
+
parameters = random_parameters(instruction)
|
64
|
+
|
65
|
+
cpu_state_before = CPUState.new
|
66
|
+
cpu_state_after = CPUState.new
|
67
|
+
|
68
|
+
buffer.reset
|
69
|
+
X64.emit_stack_frame buffer do
|
70
|
+
cpu_state_before.emit_store buffer
|
71
|
+
instruction.encode parameters, buffer, basic: true
|
72
|
+
cpu_state_after.emit_store buffer
|
73
|
+
end
|
74
|
+
|
75
|
+
begin
|
76
|
+
buffer.execute!
|
77
|
+
|
78
|
+
cpu_state_diff = cpu_state_before.xor cpu_state_after
|
79
|
+
|
80
|
+
written_registers = cpu_state_diff.to_h.select do |register, value|
|
81
|
+
# Ignore for now
|
82
|
+
# MXCSR is almost never checked
|
83
|
+
# and does not really affect code flow
|
84
|
+
register != :mxcsr && !value.all? { |v| v == 0 }
|
85
|
+
end.map(&:first)
|
86
|
+
|
87
|
+
expected_written_registers = accessed_registers(written_registers, instruction, parameters, :write).keys
|
88
|
+
unexpected_written_registers = written_registers - expected_written_registers
|
89
|
+
|
90
|
+
assert_empty unexpected_written_registers, "No operand found that writes to #{unexpected_written_registers} ("\
|
91
|
+
"(#{instruction.name} #{parameters.inspect})."\
|
92
|
+
"The following registers have been written to #{written_registers}"
|
93
|
+
|
94
|
+
#buffer.__log__ :warn
|
95
|
+
rescue ExceptionError
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.define_read_test(instruction)
|
102
|
+
define_method :"test_#{instruction.name}_reads" do
|
103
|
+
buffer = Evoasm::Buffer.new 1024, :mmap
|
104
|
+
|
105
|
+
20.times do
|
106
|
+
parameters = random_parameters(instruction)
|
107
|
+
|
108
|
+
cpu_state_before = CPUState.new
|
109
|
+
read_registers = accessed_registers(X64.registers, instruction, parameters, :read)
|
110
|
+
read_registers.each do |read_register, _|
|
111
|
+
cpu_state_before[read_register] = Array.new(4) { rand(999999999) }
|
112
|
+
end
|
113
|
+
#p [instruction.name, accessed_registers(X64.registers, instruction, parameters, :read)]
|
114
|
+
|
115
|
+
non_read_registers = X64.registers - accessed_registers(X64.registers, instruction, parameters, :read).keys
|
116
|
+
written_registers = accessed_registers(X64.registers, instruction, parameters, :write)
|
117
|
+
expected_cpu_state_after = nil
|
118
|
+
|
119
|
+
20.times do
|
120
|
+
non_read_registers.each do |non_read_register|
|
121
|
+
value = Array.new(4) { rand(999999999) }
|
122
|
+
#next if non_read_register == :rflags
|
123
|
+
#p [non_read_register, value]
|
124
|
+
cpu_state_before[non_read_register] = value
|
125
|
+
end
|
126
|
+
#parameters = random_parameters(instruction)
|
127
|
+
|
128
|
+
#p cpu_state_before.get :rflags
|
129
|
+
|
130
|
+
buffer.reset
|
131
|
+
|
132
|
+
cpu_state_after = CPUState.new
|
133
|
+
X64.emit_stack_frame buffer do
|
134
|
+
cpu_state_before.emit_load buffer
|
135
|
+
instruction.encode parameters, buffer, basic: true
|
136
|
+
cpu_state_after.emit_store buffer
|
137
|
+
end
|
138
|
+
|
139
|
+
begin
|
140
|
+
#buffer.__log__ :warn
|
141
|
+
buffer.execute!
|
142
|
+
|
143
|
+
if expected_cpu_state_after
|
144
|
+
written_registers.each do |written_register, written_word|
|
145
|
+
next if written_register == :rflags
|
146
|
+
message = "#{written_register} mismatch (#{cpu_state_before[written_register]})"\
|
147
|
+
"#{non_read_registers}"
|
148
|
+
#p [instruction.name, parameters, cpu_state_after.to_h]
|
149
|
+
assert_equal expected_cpu_state_after[written_register, written_word], cpu_state_after[written_register, written_word], message
|
150
|
+
end
|
151
|
+
else
|
152
|
+
#p ["pre", instruction.name, parameters, cpu_state_after.to_h]
|
153
|
+
expected_cpu_state_after = cpu_state_after
|
154
|
+
end
|
155
|
+
|
156
|
+
rescue ExceptionError
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
X64.instruction_names(:rflags, :mxcsr, :gp, :mm, :xmm, :zmm).each do |instruction_name|
|
165
|
+
instruction = Evoasm::X64.instruction instruction_name
|
166
|
+
next unless instruction.basic?
|
167
|
+
next if instruction.name == :std
|
168
|
+
|
169
|
+
define_write_test instruction
|
170
|
+
|
171
|
+
next if instruction.name =~ /^cmov/
|
172
|
+
#next if instruction.name =~ /^pins/
|
173
|
+
define_read_test instruction
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,780 @@
|
|
1
|
+
require 'evoasm/test'
|
2
|
+
|
3
|
+
require 'x64_helper'
|
4
|
+
|
5
|
+
module Evoasm
|
6
|
+
module X64
|
7
|
+
class InstructionEncodingTest < Minitest::Test
|
8
|
+
include X64Helper
|
9
|
+
|
10
|
+
def setup
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.test_order
|
15
|
+
:alpha
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_simd_cmp
|
19
|
+
assert_disassembles_to 'cmpeqpd xmm0, xmm1', :cmppd_xmm_xmmm128_imm8,
|
20
|
+
reg0: :xmm0, reg1: :xmm1, imm0: 0
|
21
|
+
|
22
|
+
assert_disassembles_to 'cmpeqps xmm0, xmm1', :cmpps_xmm_xmmm128_imm8,
|
23
|
+
reg0: :xmm0, reg1: :xmm1, imm0: 0
|
24
|
+
|
25
|
+
assert_disassembles_to 'cmpeqsd xmm0, xmm1', :cmpsd_xmm_xmmm64_imm8,
|
26
|
+
reg0: :xmm0, reg1: :xmm1, imm0: 0
|
27
|
+
|
28
|
+
assert_disassembles_to 'cmpeqss xmm0, xmm1', :cmpss_xmm_xmmm32_imm8,
|
29
|
+
reg0: :xmm0, reg1: :xmm1, imm0: 0
|
30
|
+
|
31
|
+
|
32
|
+
assert_disassembles_to 'vcmpeqpd xmm0, xmm1, xmm2', :vcmppd_xmm_xmm_xmmm128_imm8,
|
33
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 0
|
34
|
+
|
35
|
+
assert_disassembles_to 'vcmpeqps xmm0, xmm1, xmm2', :vcmpps_xmm_xmm_xmmm128_imm8,
|
36
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 0
|
37
|
+
|
38
|
+
assert_disassembles_to 'vcmpeqsd xmm0, xmm1, xmm2', :vcmpsd_xmm_xmm_xmmm64_imm8,
|
39
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 0
|
40
|
+
|
41
|
+
assert_disassembles_to 'vcmpeqss xmm0, xmm1, xmm2', :vcmpss_xmm_xmm_xmmm32_imm8,
|
42
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 0
|
43
|
+
|
44
|
+
|
45
|
+
assert_disassembles_to 'vcmpeqpd ymm0, ymm1, ymm2', :vcmppd_ymm_ymm_ymmm256_imm8,
|
46
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 0
|
47
|
+
|
48
|
+
assert_disassembles_to 'vcmpeqps ymm0, ymm1, ymm2', :vcmpps_ymm_ymm_ymmm256_imm8,
|
49
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 0
|
50
|
+
|
51
|
+
|
52
|
+
assert_disassembles_to 'cmpordpd xmm0, xmm1', :cmppd_xmm_xmmm128_imm8,
|
53
|
+
reg0: :xmm0, reg1: :xmm1, imm0: 7
|
54
|
+
|
55
|
+
assert_disassembles_to 'cmpordps xmm0, xmm1', :cmpps_xmm_xmmm128_imm8,
|
56
|
+
reg0: :xmm0, reg1: :xmm1, imm0: 7
|
57
|
+
|
58
|
+
assert_disassembles_to 'cmpordsd xmm0, xmm1', :cmpsd_xmm_xmmm64_imm8,
|
59
|
+
reg0: :xmm0, reg1: :xmm1, imm0: 7
|
60
|
+
|
61
|
+
assert_disassembles_to 'cmpordss xmm0, xmm1', :cmpss_xmm_xmmm32_imm8,
|
62
|
+
reg0: :xmm0, reg1: :xmm1, imm0: 7
|
63
|
+
|
64
|
+
|
65
|
+
assert_disassembles_to 'vcmpordpd xmm0, xmm1, xmm2', :vcmppd_xmm_xmm_xmmm128_imm8,
|
66
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 7
|
67
|
+
|
68
|
+
assert_disassembles_to 'vcmpordps xmm0, xmm1, xmm2', :vcmpps_xmm_xmm_xmmm128_imm8,
|
69
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 7
|
70
|
+
|
71
|
+
assert_disassembles_to 'vcmpordsd xmm0, xmm1, xmm2', :vcmpsd_xmm_xmm_xmmm64_imm8,
|
72
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 7
|
73
|
+
|
74
|
+
assert_disassembles_to 'vcmpordss xmm0, xmm1, xmm2', :vcmpss_xmm_xmm_xmmm32_imm8,
|
75
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 7
|
76
|
+
|
77
|
+
|
78
|
+
assert_disassembles_to 'vcmpordpd ymm0, ymm1, ymm2', :vcmppd_ymm_ymm_ymmm256_imm8,
|
79
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 7
|
80
|
+
|
81
|
+
|
82
|
+
assert_disassembles_to 'vcmpordps ymm0, ymm1, ymm2', :vcmpps_ymm_ymm_ymmm256_imm8,
|
83
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 7
|
84
|
+
|
85
|
+
|
86
|
+
assert_disassembles_to 'vcmptrue_uspd xmm0, xmm1, xmm2', :vcmppd_xmm_xmm_xmmm128_imm8,
|
87
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 0x1f
|
88
|
+
|
89
|
+
assert_disassembles_to 'vcmptrue_usps xmm0, xmm1, xmm2', :vcmpps_xmm_xmm_xmmm128_imm8,
|
90
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 0x1f
|
91
|
+
|
92
|
+
assert_disassembles_to 'vcmptrue_ussd xmm0, xmm1, xmm2', :vcmpsd_xmm_xmm_xmmm64_imm8,
|
93
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 0x1f
|
94
|
+
|
95
|
+
assert_disassembles_to 'vcmptrue_uspd ymm0, ymm1, ymm2', :vcmppd_ymm_ymm_ymmm256_imm8,
|
96
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 0x1f
|
97
|
+
|
98
|
+
assert_disassembles_to 'vcmptrue_usps ymm0, ymm1, ymm2', :vcmpps_ymm_ymm_ymmm256_imm8,
|
99
|
+
reg0: :xmm0, reg1: :xmm1, reg2: :xmm2, imm0: 0x1f
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_cmps
|
104
|
+
assert_assembles_to "\xA6", :cmpsb
|
105
|
+
assert_assembles_to "\x66\xA7", :cmpsw
|
106
|
+
assert_assembles_to "\xA7", :cmpsd
|
107
|
+
assert_assembles_to "\x48\xA7", :cmpsq
|
108
|
+
|
109
|
+
assert_assembles_to "\xF3\xA6", :repe_cmpsb
|
110
|
+
assert_assembles_to "\xF3\x66\xA7", :repe_cmpsw
|
111
|
+
assert_assembles_to "\xF3\xA7", :repe_cmpsd
|
112
|
+
assert_assembles_to "\xF3\x48\xA7", :repe_cmpsq
|
113
|
+
|
114
|
+
assert_assembles_to "\xF2\xA6", :repne_cmpsb
|
115
|
+
assert_assembles_to "\xF2\x66\xA7", :repne_cmpsw
|
116
|
+
assert_assembles_to "\xF2\xA7", :repne_cmpsd
|
117
|
+
assert_assembles_to "\xF2\x48\xA7", :repne_cmpsq
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_lods
|
121
|
+
assert_assembles_to "\xAC", :lodsb
|
122
|
+
assert_assembles_to "\x66\xAD", :lodsw
|
123
|
+
assert_assembles_to "\xAD", :lodsd
|
124
|
+
assert_assembles_to "\x48\xAD", :lodsq
|
125
|
+
|
126
|
+
assert_assembles_to "\xF3\xAC", :rep_lodsb
|
127
|
+
assert_assembles_to "\xF3\x66\xAD", :rep_lodsw
|
128
|
+
assert_assembles_to "\xF3\xAD", :rep_lodsd
|
129
|
+
assert_assembles_to "\xF3\x48\xAD", :rep_lodsq
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_movs
|
133
|
+
assert_assembles_to "\xA4", :movsb
|
134
|
+
assert_assembles_to "\x66\xA5", :movsw
|
135
|
+
assert_assembles_to "\xA5", :movsd
|
136
|
+
assert_assembles_to "\x48\xA5", :movsq
|
137
|
+
|
138
|
+
assert_assembles_to "\xF3\xA4", :rep_movsb
|
139
|
+
assert_assembles_to "\xF3\x66\xA5", :rep_movsw
|
140
|
+
assert_assembles_to "\xF3\xA5", :rep_movsd
|
141
|
+
assert_assembles_to "\xF3\x48\xA5", :rep_movsq
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_scas
|
145
|
+
assert_assembles_to "\xAE", :scasb
|
146
|
+
assert_assembles_to "\x66\xAF", :scasw
|
147
|
+
assert_assembles_to "\xAF", :scasd
|
148
|
+
assert_assembles_to "\x48\xAF", :scasq
|
149
|
+
|
150
|
+
assert_assembles_to "\xF3\xAE", :repe_scasb
|
151
|
+
assert_assembles_to "\xF3\x66\xAF", :repe_scasw
|
152
|
+
assert_assembles_to "\xF3\xAF", :repe_scasd
|
153
|
+
assert_assembles_to "\xF3\x48\xAF", :repe_scasq
|
154
|
+
|
155
|
+
assert_assembles_to "\xF2\xAE", :repne_scasb
|
156
|
+
assert_assembles_to "\xF2\x66\xAF", :repne_scasw
|
157
|
+
assert_assembles_to "\xF2\xAF", :repne_scasd
|
158
|
+
assert_assembles_to "\xF2\x48\xAF", :repne_scasq
|
159
|
+
end
|
160
|
+
|
161
|
+
def test_stos
|
162
|
+
assert_assembles_to "\xAA", :stosb
|
163
|
+
assert_assembles_to "\x66\xAB", :stosw
|
164
|
+
assert_assembles_to "\xAB", :stosd
|
165
|
+
assert_assembles_to "\x48\xAB", :stosq
|
166
|
+
|
167
|
+
assert_assembles_to "\xF3\xAA", :rep_stosb
|
168
|
+
assert_assembles_to "\xF3\x66\xAB", :rep_stosw
|
169
|
+
assert_assembles_to "\xF3\xAB", :rep_stosd
|
170
|
+
assert_assembles_to "\xF3\x48\xAB", :rep_stosq
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_nop
|
174
|
+
assert_assembles_to "\x66\x0F\x1F\xC0", :nop_rm16, reg0: :a
|
175
|
+
assert_assembles_to "\x0F\x1F\xC0", :nop_rm32, reg0: :a
|
176
|
+
end
|
177
|
+
|
178
|
+
def test_movq_mm
|
179
|
+
assert_assembles_to "\x48\x0F\x6E\xC0", :movq_mm_rm64, reg0: :mm0, reg1: :a
|
180
|
+
assert_assembles_to "\x48\x0F\x6E\x00", :movq_mm_rm64, reg0: :mm0, reg_base: :a
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_movd_mm
|
184
|
+
assert_assembles_to "\x0F\x6E\xC0", :movd_mm_rm32, reg0: :mm0, reg1: :a
|
185
|
+
assert_assembles_to "\x0F\x6E\x00", :movd_mm_rm32, reg0: :mm0, reg_base: :a
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_movq_xmm
|
189
|
+
assert_assembles_to "\x66\x48\x0F\x6E\xC0", :movq_xmm_rm64, reg0: :xmm0, reg1: :a
|
190
|
+
assert_assembles_to "\x66\x49\x0F\x6E\xC4", :movq_xmm_rm64, reg0: :xmm0, reg1: :r12
|
191
|
+
assert_assembles_to "\x66\x4D\x0F\x6E\xE4", :movq_xmm_rm64, reg0: :xmm12, reg1: :r12
|
192
|
+
|
193
|
+
assert_assembles_to "\x66\x4D\x0F\x6E\x24\x24", :movq_xmm_rm64, reg0: :xmm12, reg_base: :r12
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_vmovq
|
197
|
+
assert_assembles_to "\xC4\xE1\xF9\x6E\xC0", :vmovq_xmm_rm64, reg0: :xmm0, reg1: :a
|
198
|
+
assert_assembles_to "\xC4\xC1\xF9\x6E\xC4", :vmovq_xmm_rm64, reg0: :xmm0, reg1: :r12
|
199
|
+
assert_assembles_to "\xC4\x41\xF9\x6E\xE4", :vmovq_xmm_rm64, reg0: :xmm12, reg1: :r12
|
200
|
+
assert_assembles_to "\xC4\x41\xF9\x6E\x24\x24", :vmovq_xmm_rm64, reg0: :xmm12, reg_base: :r12
|
201
|
+
|
202
|
+
assert_assembles_to "\xC4\xE1\xF9\x7E\xC0", :vmovq_rm64_xmm, reg0: :a, reg1: :xmm0
|
203
|
+
assert_assembles_to "\xC4\xC1\xF9\x7E\xC4", :vmovq_rm64_xmm, reg0: :r12, reg1: :xmm0
|
204
|
+
assert_assembles_to "\xC4\x41\xF9\x7E\xE4", :vmovq_rm64_xmm, reg0: :r12, reg1: :xmm12
|
205
|
+
assert_assembles_to "\xC4\x41\xF9\x7E\x24\x24", :vmovq_rm64_xmm, reg_base: :r12, reg1: :xmm12
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_movd_xmm
|
209
|
+
assert_assembles_to "\x66\x0F\x6E\xC0", :movd_xmm_rm32, reg0: :xmm0, reg1: :a
|
210
|
+
assert_assembles_to "\x66\x41\x0F\x6E\xC4", :movd_xmm_rm32, reg0: :xmm0, reg1: :r12
|
211
|
+
assert_assembles_to "\x66\x45\x0F\x6E\xE4", :movd_xmm_rm32, reg0: :xmm12, reg1: :r12
|
212
|
+
|
213
|
+
assert_assembles_to "\x66\x45\x0F\x6E\x24\x24", :movd_xmm_rm32, reg0: :xmm12, reg_base: :r12
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_xchg_implicit
|
217
|
+
assert_assembles_to "\x66\x93", :xchg_ax_r16, reg0: :b
|
218
|
+
assert_assembles_to "\x93", :xchg_eax_r32, reg0: :b
|
219
|
+
assert_assembles_to "\x48\x93", :xchg_rax_r64, reg0: :b
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_prefetchwt1
|
223
|
+
assert_assembles_to "\x0F\x0D\x10", :prefetchwt1_m8, reg_base: :a
|
224
|
+
assert_assembles_to "\x67\x0F\x0D\x10", :prefetchwt1_m8, reg_base: :a, addr_size: 32
|
225
|
+
end
|
226
|
+
|
227
|
+
def test_lea_rip
|
228
|
+
assert_disassembles_to 'lea rax, qword ptr [rip]', :lea_r64_m64, reg0: :a, reg_base: :ip
|
229
|
+
end
|
230
|
+
|
231
|
+
SIMD_CMP_INSTRUCTION_NAMES = %i(
|
232
|
+
vcmpps_xmm_xmm_xmmm128_imm8
|
233
|
+
vcmppd_xmm_xmm_xmmm128_imm8
|
234
|
+
vcmpps_ymm_ymm_ymmm256_imm8
|
235
|
+
vcmppd_ymm_ymm_ymmm256_imm8
|
236
|
+
vcmpsd_xmm_xmm_xmmm64_imm8
|
237
|
+
vcmpss_xmm_xmm_xmmm32_imm8
|
238
|
+
cmpps_xmm_xmmm128_imm8
|
239
|
+
cmppd_xmm_xmmm128_imm8
|
240
|
+
cmpsd_xmm_xmmm64_imm8
|
241
|
+
cmpss_xmm_xmmm32_imm8
|
242
|
+
).freeze
|
243
|
+
|
244
|
+
NOP_INSTRUCTION_NAMES = %i(
|
245
|
+
nop_rm16
|
246
|
+
nop_rm32
|
247
|
+
).freeze
|
248
|
+
|
249
|
+
XCHG_IMPLICIT_INSTRUCTION_NAMES = %i(
|
250
|
+
xchg_ax_r16
|
251
|
+
xchg_eax_r32
|
252
|
+
xchg_rax_r64
|
253
|
+
).freeze
|
254
|
+
|
255
|
+
STRING_INSTRUCTION_NAMES = %i(
|
256
|
+
cmpsb
|
257
|
+
cmpsw
|
258
|
+
cmpsd
|
259
|
+
cmpsq
|
260
|
+
repe_cmpsb
|
261
|
+
repe_cmpsw
|
262
|
+
repe_cmpsd
|
263
|
+
repe_cmpsq
|
264
|
+
repne_cmpsb
|
265
|
+
repne_cmpsw
|
266
|
+
repne_cmpsd
|
267
|
+
repne_cmpsq
|
268
|
+
lodsb
|
269
|
+
lodsw
|
270
|
+
lodsd
|
271
|
+
lodsq
|
272
|
+
rep_lodsb
|
273
|
+
rep_lodsw
|
274
|
+
rep_lodsd
|
275
|
+
rep_lodsq
|
276
|
+
movsb
|
277
|
+
movsw
|
278
|
+
movsd
|
279
|
+
movsq
|
280
|
+
rep_movsb
|
281
|
+
rep_movsw
|
282
|
+
rep_movsd
|
283
|
+
rep_movsq
|
284
|
+
scasb
|
285
|
+
scasw
|
286
|
+
scasd
|
287
|
+
scasq
|
288
|
+
repe_scasb
|
289
|
+
repe_scasw
|
290
|
+
repe_scasd
|
291
|
+
repe_scasq
|
292
|
+
repne_scasb
|
293
|
+
repne_scasw
|
294
|
+
repne_scasd
|
295
|
+
repne_scasq
|
296
|
+
stosb
|
297
|
+
stosw
|
298
|
+
stosd
|
299
|
+
stosq
|
300
|
+
rep_stosb
|
301
|
+
rep_stosw
|
302
|
+
rep_stosd
|
303
|
+
rep_stosq
|
304
|
+
).freeze
|
305
|
+
|
306
|
+
MOVQ_MOVD_INSTRUCTION_NAMES = %i(
|
307
|
+
movq_mm_rm64
|
308
|
+
movq_rm64_mm
|
309
|
+
movd_mm_rm32
|
310
|
+
movd_rm32_mm
|
311
|
+
movq_xmm_rm64
|
312
|
+
movq_rm64_xmm
|
313
|
+
movd_xmm_rm32
|
314
|
+
movd_rm32_xmm
|
315
|
+
vmovq_xmm_rm64
|
316
|
+
vmovq_rm64_xmm
|
317
|
+
).freeze
|
318
|
+
|
319
|
+
UNSUPPORTED_INSTRUCTION_NAMES = %i(
|
320
|
+
prefetchwt1_m8
|
321
|
+
rdpid_r64
|
322
|
+
)
|
323
|
+
|
324
|
+
class InstructionTest
|
325
|
+
attr_reader :instruction
|
326
|
+
|
327
|
+
class ActualOperand
|
328
|
+
|
329
|
+
REGISTERS = {
|
330
|
+
a: {
|
331
|
+
8 => 'al',
|
332
|
+
16 => 'ax',
|
333
|
+
32 => 'eax',
|
334
|
+
64 => 'rax'
|
335
|
+
},
|
336
|
+
b: {
|
337
|
+
8 => 'bl',
|
338
|
+
16 => 'bx',
|
339
|
+
32 => 'ebx',
|
340
|
+
64 => 'rbx'
|
341
|
+
},
|
342
|
+
c: {
|
343
|
+
8 => 'cl',
|
344
|
+
16 => 'cx',
|
345
|
+
32 => 'ecx',
|
346
|
+
64 => 'rcx'
|
347
|
+
},
|
348
|
+
bp: {
|
349
|
+
8 => 'bpl',
|
350
|
+
16 => 'bp',
|
351
|
+
32 => 'ebp',
|
352
|
+
64 => 'rbp'
|
353
|
+
},
|
354
|
+
sp: {
|
355
|
+
8 => 'spl',
|
356
|
+
16 => 'sp',
|
357
|
+
32 => 'esp',
|
358
|
+
64 => 'rsp'
|
359
|
+
},
|
360
|
+
r11: {
|
361
|
+
8 => 'r11b',
|
362
|
+
16 => 'r11w',
|
363
|
+
32 => 'r11d',
|
364
|
+
64 => 'r11'
|
365
|
+
},
|
366
|
+
r12: {
|
367
|
+
8 => 'r12b',
|
368
|
+
16 => 'r12w',
|
369
|
+
32 => 'r12d',
|
370
|
+
64 => 'r12'
|
371
|
+
},
|
372
|
+
xmm0: {
|
373
|
+
128 => 'xmm0',
|
374
|
+
256 => 'ymm0'
|
375
|
+
},
|
376
|
+
xmm1: {
|
377
|
+
128 => 'xmm1',
|
378
|
+
256 => 'ymm1'
|
379
|
+
},
|
380
|
+
xmm2: {
|
381
|
+
128 => 'xmm2',
|
382
|
+
256 => 'ymm2'
|
383
|
+
},
|
384
|
+
xmm3: {
|
385
|
+
128 => 'xmm3',
|
386
|
+
256 => 'ymm3'
|
387
|
+
},
|
388
|
+
xmm10: {
|
389
|
+
128 => 'xmm10',
|
390
|
+
256 => 'ymm10'
|
391
|
+
},
|
392
|
+
xmm11: {
|
393
|
+
128 => 'xmm11',
|
394
|
+
256 => 'ymm11'
|
395
|
+
},
|
396
|
+
mm0: {
|
397
|
+
64 => 'mm0'
|
398
|
+
},
|
399
|
+
mm1: {
|
400
|
+
64 => 'mm1'
|
401
|
+
},
|
402
|
+
mm7: {
|
403
|
+
64 => 'mm7'
|
404
|
+
}
|
405
|
+
}.freeze
|
406
|
+
|
407
|
+
attr_reader :parameter_names
|
408
|
+
attr_reader :parameter_values
|
409
|
+
attr_reader :type, :size
|
410
|
+
|
411
|
+
def initialize(type, parameter_names, parameter_values, size = nil, register_size = nil)
|
412
|
+
@type = type
|
413
|
+
@parameter_names = Array(parameter_names)
|
414
|
+
@parameter_values = Array(parameter_values)
|
415
|
+
@size = size
|
416
|
+
@register_size = register_size
|
417
|
+
end
|
418
|
+
|
419
|
+
def parameter_name
|
420
|
+
@parameter_names.first
|
421
|
+
end
|
422
|
+
|
423
|
+
def parameter_value
|
424
|
+
@parameter_values.first
|
425
|
+
end
|
426
|
+
|
427
|
+
def disassembly(encoded_instruction = nil)
|
428
|
+
send :"#{type}_disassembly", encoded_instruction
|
429
|
+
end
|
430
|
+
|
431
|
+
private
|
432
|
+
|
433
|
+
def imm_disassembly(encoded_instruction)
|
434
|
+
value = parameter_value
|
435
|
+
value += encoded_instruction.bytesize if parameter_name == :rel
|
436
|
+
|
437
|
+
if value == 1
|
438
|
+
value.to_s
|
439
|
+
else
|
440
|
+
"0x#{value.to_s(16)}"
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
def reg_disassembly(encoded_instruction)
|
445
|
+
REGISTERS.fetch(parameter_value).fetch(size)
|
446
|
+
end
|
447
|
+
|
448
|
+
def mem_disassembly(encoded_instruction)
|
449
|
+
pointer =
|
450
|
+
if parameter_name == :moffs
|
451
|
+
"0x#{parameter_value.to_s(16)}"
|
452
|
+
else
|
453
|
+
|
454
|
+
base_register, index_register =
|
455
|
+
REGISTERS.values_at(*parameter_values.values_at(0, 1)).map do |sizes|
|
456
|
+
next nil if sizes.nil?
|
457
|
+
if sizes.key? 64
|
458
|
+
sizes[64]
|
459
|
+
else
|
460
|
+
sizes[@register_size]
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
scale = parameter_values[2]
|
465
|
+
|
466
|
+
sib = ''
|
467
|
+
sib << base_register
|
468
|
+
sib << " + #{index_register}" if index_register
|
469
|
+
sib << "*#{scale}" if scale && scale != 1
|
470
|
+
|
471
|
+
sib
|
472
|
+
end
|
473
|
+
|
474
|
+
"#{pointer_size} ptr [#{pointer}]"
|
475
|
+
end
|
476
|
+
|
477
|
+
def pointer_size
|
478
|
+
case size
|
479
|
+
when 8
|
480
|
+
'byte'
|
481
|
+
when 16
|
482
|
+
'word'
|
483
|
+
when 32
|
484
|
+
'dword'
|
485
|
+
when 64
|
486
|
+
'qword'
|
487
|
+
when 128
|
488
|
+
'xmmword'
|
489
|
+
when 256
|
490
|
+
'ymmword'
|
491
|
+
when nil
|
492
|
+
''
|
493
|
+
else
|
494
|
+
raise "invalid pointer size #{size}"
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
end
|
499
|
+
|
500
|
+
class ActualOperands
|
501
|
+
attr_reader :formal_operand
|
502
|
+
|
503
|
+
REGISTERS = {
|
504
|
+
gp: %i(a c b sp bp r11 r12),
|
505
|
+
xmm: %i(xmm0 xmm1 xmm10 xmm11),
|
506
|
+
mm: %i(mm0 mm1 mm7)
|
507
|
+
}.freeze
|
508
|
+
|
509
|
+
SKIP_IMPLICIT_XMM0_INSTRUCTION_NAMES = %i(
|
510
|
+
blendvpd_xmm_xmmm128_xmm0
|
511
|
+
blendvps_xmm_xmmm128_xmm0
|
512
|
+
pblendvb_xmm_xmmm128_xmm0
|
513
|
+
sha256rnds2_xmm_xmmm128_xmm0
|
514
|
+
).freeze
|
515
|
+
|
516
|
+
IMMEDIATE_VALUES = {
|
517
|
+
imm: 0x4a,
|
518
|
+
imm0: 0x4a,
|
519
|
+
imm1: 0x4b,
|
520
|
+
rel: 0x4c,
|
521
|
+
moffs: 0x4d,
|
522
|
+
}.freeze
|
523
|
+
|
524
|
+
def initialize(formal_operand, basic:)
|
525
|
+
@formal_operand = formal_operand
|
526
|
+
@actual_operands = []
|
527
|
+
@basic = basic
|
528
|
+
|
529
|
+
load
|
530
|
+
end
|
531
|
+
|
532
|
+
def basic?
|
533
|
+
@basic
|
534
|
+
end
|
535
|
+
|
536
|
+
def parameter_name
|
537
|
+
formal_operand.parameter&.name
|
538
|
+
end
|
539
|
+
|
540
|
+
def to_a
|
541
|
+
@actual_operands
|
542
|
+
end
|
543
|
+
|
544
|
+
def empty?
|
545
|
+
@actual_operands.empty?
|
546
|
+
end
|
547
|
+
|
548
|
+
private
|
549
|
+
|
550
|
+
def load
|
551
|
+
if formal_operand.mnemonic?
|
552
|
+
case formal_operand.type
|
553
|
+
when :reg
|
554
|
+
add_register_operand
|
555
|
+
when :rm
|
556
|
+
add_register_operand
|
557
|
+
add_memory_operand unless basic?
|
558
|
+
when :imm
|
559
|
+
add_immediate_operand
|
560
|
+
when :mem
|
561
|
+
add_memory_operand
|
562
|
+
when :vsib
|
563
|
+
raise if basic?
|
564
|
+
add_vsib_operand
|
565
|
+
else
|
566
|
+
raise "unknown operand type #{formal_operand.type}"
|
567
|
+
end
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
def add_register_operand
|
572
|
+
if formal_operand.implicit?
|
573
|
+
register = formal_operand.register
|
574
|
+
unless register == :xmm0 && skip_implicit_xmm0?
|
575
|
+
@actual_operands << ActualOperand.new(:reg, nil,
|
576
|
+
formal_operand.register,
|
577
|
+
formal_operand.size)
|
578
|
+
end
|
579
|
+
else
|
580
|
+
raise 'missing parameter' unless formal_operand.parameter
|
581
|
+
REGISTERS.fetch(formal_operand.register_type).each do |register|
|
582
|
+
@actual_operands << ActualOperand.new(:reg, parameter_name,
|
583
|
+
register, formal_operand.size)
|
584
|
+
end
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
def add_memory_operand
|
589
|
+
if parameter_name == :moffs
|
590
|
+
@actual_operands << ActualOperand.new(:mem,
|
591
|
+
parameter_name,
|
592
|
+
IMMEDIATE_VALUES.fetch(parameter_name),
|
593
|
+
memory_size)
|
594
|
+
else
|
595
|
+
REGISTERS.fetch(:gp).each do |register|
|
596
|
+
@actual_operands << ActualOperand.new(:mem,
|
597
|
+
[:reg_base],
|
598
|
+
[register],
|
599
|
+
memory_size)
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
def add_vsib_operand
|
605
|
+
@actual_operands << ActualOperand.new(:mem,
|
606
|
+
[:reg_base, :reg_index, :scale],
|
607
|
+
[:a, :xmm0, 1],
|
608
|
+
memory_size,
|
609
|
+
formal_operand.index_register_size)
|
610
|
+
end
|
611
|
+
|
612
|
+
def add_immediate_operand
|
613
|
+
@actual_operands <<
|
614
|
+
if formal_operand.implicit?
|
615
|
+
ActualOperand.new(:imm, parameter_name, formal_operand.immediate)
|
616
|
+
else
|
617
|
+
ActualOperand.new(:imm, parameter_name, IMMEDIATE_VALUES.fetch(parameter_name))
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
def skip_implicit_xmm0?
|
622
|
+
SKIP_IMPLICIT_XMM0_INSTRUCTION_NAMES.include? formal_operand.instruction.name
|
623
|
+
end
|
624
|
+
|
625
|
+
def memory_size
|
626
|
+
# Work around bugs in Capstone
|
627
|
+
# which sometimes reports wrong pointer sizes
|
628
|
+
# FIXME: recheck if we really got them right
|
629
|
+
case formal_operand.instruction.name
|
630
|
+
when :comisd_xmm_xmmm64
|
631
|
+
128
|
632
|
+
when :comiss_xmm_xmmm32
|
633
|
+
128
|
634
|
+
when :punpcklbw_mm_mmm32,
|
635
|
+
:punpckldq_mm_mmm32,
|
636
|
+
:punpcklwd_mm_mmm32
|
637
|
+
64
|
638
|
+
when :vcomisd_xmm_xmmm64,
|
639
|
+
:vcomisd_xmm_xmmm32,
|
640
|
+
:vcomiss_xmm_xmmm32
|
641
|
+
128
|
642
|
+
when :vpmovsxbd_ymm_xmmm64,
|
643
|
+
:vpmovsxwq_ymm_xmmm64,
|
644
|
+
:vpmovsxbd_ymm_xmmm64,
|
645
|
+
:vpmovzxbd_ymm_xmmm64,
|
646
|
+
:vpmovzxwq_ymm_xmmm64
|
647
|
+
32
|
648
|
+
when :vpmovsxbq_ymm_xmmm32,
|
649
|
+
:vpmovzxbq_ymm_xmmm32
|
650
|
+
16
|
651
|
+
else
|
652
|
+
formal_operand.memory_size
|
653
|
+
end
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
def test_rdpid
|
658
|
+
skip 'missing'
|
659
|
+
end
|
660
|
+
|
661
|
+
def initialize(test_class, instruction)
|
662
|
+
@test_class = test_class
|
663
|
+
@instruction = instruction
|
664
|
+
end
|
665
|
+
|
666
|
+
def mnemonics
|
667
|
+
case instruction.name
|
668
|
+
when :clflushopt_m8
|
669
|
+
# Capstone, might be a bug
|
670
|
+
%w(clflush)
|
671
|
+
when :vcvtpd2dq_xmm_xmmm128
|
672
|
+
%w(vcvtpd2dqx vcvtpd2dq)
|
673
|
+
when :vcvtpd2ps_xmm_xmmm128
|
674
|
+
%w(vcvtpd2ps vcvtpd2psx)
|
675
|
+
when :vcvttpd2dq_xmm_xmmm128
|
676
|
+
%w(vcvttpd2dqx vcvttpd2dq)
|
677
|
+
else
|
678
|
+
instruction.mnemonics
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
682
|
+
def run(test, basic:)
|
683
|
+
return if basic && !instruction.basic?
|
684
|
+
|
685
|
+
# Capstone prints rm operand last
|
686
|
+
operands = instruction.operands.map do |operand|
|
687
|
+
ActualOperands.new operand, basic: basic
|
688
|
+
end.reject(&:empty?)
|
689
|
+
|
690
|
+
combinations =
|
691
|
+
if operands.empty?
|
692
|
+
[[]]
|
693
|
+
else
|
694
|
+
combinations =
|
695
|
+
operands.first.to_a.product(*(operands[1..-1] || []).map(&:to_a))
|
696
|
+
end
|
697
|
+
|
698
|
+
raise if combinations.empty?
|
699
|
+
combinations.each do |combination|
|
700
|
+
parameters = Evoasm::X64::Parameters.new(basic: basic)
|
701
|
+
combination.each do |operand|
|
702
|
+
operand.parameter_names.zip(operand.parameter_values) do |name, value|
|
703
|
+
parameters[name] = value
|
704
|
+
test.assert_equal value, parameters[name]
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
encoded_instruction = instruction.encode parameters, basic: basic
|
709
|
+
|
710
|
+
# Capstone gives operands in wrong order
|
711
|
+
# Oddly, only if both operands are registers
|
712
|
+
if instruction.name =~ /^test_rm\d+_r\d+/ &&
|
713
|
+
combination.all? { |operand| operand.type == :reg }
|
714
|
+
combination.reverse!
|
715
|
+
end
|
716
|
+
|
717
|
+
operands_disassembly = combination.map do |operand|
|
718
|
+
operand.disassembly encoded_instruction
|
719
|
+
end
|
720
|
+
|
721
|
+
expected_disassemblys = mnemonics.map do |mnemonic|
|
722
|
+
"#{mnemonic.downcase} #{operands_disassembly.join(', ')}"
|
723
|
+
end
|
724
|
+
|
725
|
+
test.assert_includes expected_disassemblys, test.disassemble(encoded_instruction)
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
def define!(basic:)
|
730
|
+
run_method = method(:run)
|
731
|
+
|
732
|
+
@test_class.send :define_method, test_method_name(basic: basic) do
|
733
|
+
run_method.call self, basic: basic
|
734
|
+
end
|
735
|
+
end
|
736
|
+
|
737
|
+
private
|
738
|
+
|
739
|
+
def test_method_name(basic:)
|
740
|
+
method_name = "test_#{instruction.name}"
|
741
|
+
method_name << '_basic' if basic
|
742
|
+
|
743
|
+
method_name
|
744
|
+
end
|
745
|
+
end
|
746
|
+
|
747
|
+
Evoasm::Libevoasm.enum_type(:x64_inst_id).symbols.each do |instruction_name|
|
748
|
+
next if instruction_name == :none
|
749
|
+
# Capstone uses pseudo-mnemonics, tested separately
|
750
|
+
next if SIMD_CMP_INSTRUCTION_NAMES.include? instruction_name
|
751
|
+
|
752
|
+
# Capstone does not like these for some reason, test separately
|
753
|
+
next if NOP_INSTRUCTION_NAMES.include? instruction_name
|
754
|
+
next if XCHG_IMPLICIT_INSTRUCTION_NAMES.include? instruction_name
|
755
|
+
|
756
|
+
# Capstone gives implicit operands for these
|
757
|
+
next if STRING_INSTRUCTION_NAMES.include? instruction_name
|
758
|
+
|
759
|
+
# Most (dis)assemblers (including Capstone) use the MOVD mnemonic for both
|
760
|
+
# movq and movd, and MOVQ (but sometimes also MOVD) for the XMM version.
|
761
|
+
# Oddly, GNU AS correctly uses the MM version if movq
|
762
|
+
# is used with register operands but does use the XMM version
|
763
|
+
# for SIB operands.
|
764
|
+
# Anyway, Capstone does not get this 100% right, or at least not
|
765
|
+
# how we need it.
|
766
|
+
next if MOVQ_MOVD_INSTRUCTION_NAMES.include? instruction_name
|
767
|
+
|
768
|
+
# not supported by Capstone
|
769
|
+
next if UNSUPPORTED_INSTRUCTION_NAMES.include? instruction_name
|
770
|
+
|
771
|
+
|
772
|
+
instruction = Evoasm::X64.instruction instruction_name
|
773
|
+
instruction_test = InstructionTest.new self, instruction
|
774
|
+
|
775
|
+
instruction_test.define! basic: false
|
776
|
+
instruction_test.define! basic: true
|
777
|
+
end
|
778
|
+
end
|
779
|
+
end
|
780
|
+
end
|