evoasm 0.0.2.pre7 → 0.1.0.pre2
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 +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
|