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
data/lib/evoasm/prng.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
module Evoasm
|
2
|
+
# A fast pseudo-random number generator.
|
3
|
+
class PRNG < FFI::AutoPointer
|
4
|
+
|
5
|
+
# Number of seed elements required
|
6
|
+
SEED_SIZE = 16
|
7
|
+
|
8
|
+
# Default seed values
|
9
|
+
DEFAULT_SEED = (1..SEED_SIZE).to_a
|
10
|
+
|
11
|
+
# @!visibility private
|
12
|
+
def self.release(ptr)
|
13
|
+
Libevoasm.prng_free ptr
|
14
|
+
end
|
15
|
+
|
16
|
+
# Gives the default {PRNG}, seeded with {DEFAULT_SEED}
|
17
|
+
# @return [PRNG] default {PRNG} instance
|
18
|
+
def self.default
|
19
|
+
@default_prng ||= new(DEFAULT_SEED)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param seed [Array<Integer>] the seed, must have exactly {SEED_SIZE} elements
|
23
|
+
# @return [PRNG] new {PRNG} instance
|
24
|
+
def initialize(seed = DEFAULT_SEED)
|
25
|
+
if seed.size != SEED_SIZE
|
26
|
+
raise ArgumentError, "seed must be have exactly #{SEED_SIZE} elements"
|
27
|
+
end
|
28
|
+
|
29
|
+
ptr = Libevoasm.prng_alloc
|
30
|
+
|
31
|
+
seed_ptr = FFI::MemoryPointer.new :uint64, SEED_SIZE
|
32
|
+
seed_ptr.write_array_of_uint64 seed
|
33
|
+
|
34
|
+
Libevoasm.prng_init ptr, seed_ptr
|
35
|
+
super(ptr)
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Integer] a random number in the range (0..2**64 - 2)
|
39
|
+
def rand64
|
40
|
+
Libevoasm.prng_rand64 self
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Integer] a random number in the range (0..2**32 - 2)
|
44
|
+
def rand32
|
45
|
+
Libevoasm.prng_rand32 self
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Integer] a random number in the range (0..2**16 - 2)
|
49
|
+
def rand16
|
50
|
+
Libevoasm.prng_rand16 self
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [Integer] a random number in the range (0..2**8 - 2)
|
54
|
+
def rand8
|
55
|
+
Libevoasm.prng_rand8 self
|
56
|
+
end
|
57
|
+
|
58
|
+
# Gives a random number in the range (+min+..+max+ - 1)
|
59
|
+
# @return [Integer] random number
|
60
|
+
def rand_between(min, max)
|
61
|
+
Libevoasm.prng_rand_between self, min, max
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/evoasm/program.rb
CHANGED
@@ -1,23 +1,206 @@
|
|
1
|
-
require '
|
1
|
+
require 'ffi'
|
2
2
|
|
3
3
|
module Evoasm
|
4
|
-
class Program
|
5
|
-
include Search::Util
|
6
4
|
|
7
|
-
|
8
|
-
|
5
|
+
# Represents a program comprising one ore multiple kernels
|
6
|
+
class Program < FFI::AutoPointer
|
7
|
+
|
8
|
+
require_relative 'program/io.rb'
|
9
|
+
|
10
|
+
|
11
|
+
# @!visibility private
|
12
|
+
def self.release(ptr)
|
13
|
+
Libevoasm.program_destroy(ptr)
|
14
|
+
Libevoasm.program_free(ptr)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
ptr = Libevoasm.program_alloc
|
19
|
+
super ptr
|
20
|
+
end
|
21
|
+
|
22
|
+
# Runs the program with the given input
|
23
|
+
# @param input_tuple [Array] an input tuple
|
24
|
+
# @return [Array] the output tuple corresponding to the given input
|
25
|
+
def run(*input_tuple)
|
26
|
+
run_all(input_tuple).first
|
9
27
|
end
|
10
28
|
|
29
|
+
# Like {#run}, but runs multiple input tuples at once
|
30
|
+
# @param input_examples [Array] an array of input tuples
|
31
|
+
# @return [Array] an array of output tuples
|
11
32
|
def run_all(*input_examples)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
33
|
+
input = Program::Input.new(input_examples)
|
34
|
+
|
35
|
+
output_ptr = Libevoasm.program_run self, input
|
36
|
+
|
37
|
+
if output_ptr.null?
|
38
|
+
raise Error.last
|
39
|
+
end
|
40
|
+
|
41
|
+
Program::Output.new(output_ptr).to_a
|
42
|
+
end
|
43
|
+
|
44
|
+
# Gives the size of the program as the number of kernels
|
45
|
+
# @return [Integer] size
|
46
|
+
def size
|
47
|
+
Libevoasm.program_get_size self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Eliminates intron instructions (instructions without effect)
|
51
|
+
# @return [Program] a new program with introns eliminated
|
52
|
+
def eliminate_introns
|
53
|
+
program = Program.new
|
54
|
+
unless Libevoasm.program_eliminate_introns self, program
|
55
|
+
raise Libevoasm::Error.last
|
56
|
+
end
|
57
|
+
|
58
|
+
program
|
59
|
+
end
|
60
|
+
|
61
|
+
# Gives the disassembly for the specified kernel
|
62
|
+
# @param kernel_index [Integer] index of kernel to disassemble
|
63
|
+
# @return [String] disassembly
|
64
|
+
def disassemble_kernel(kernel_index)
|
65
|
+
code_ptr_ptr = FFI::MemoryPointer.new :pointer
|
66
|
+
code_len = Libevoasm.program_get_kernel_code self, kernel_index, code_ptr_ptr
|
67
|
+
code_ptr = code_ptr_ptr.read_pointer
|
68
|
+
code = code_ptr.read_string(code_len)
|
69
|
+
|
70
|
+
X64.disassemble code, code_ptr.address
|
71
|
+
end
|
72
|
+
|
73
|
+
# Gives the disassembly for all kernels in the program
|
74
|
+
# @return [Array<String>] array of disassembly
|
75
|
+
def disassemble_kernels
|
76
|
+
Array.new(size) do |kernel_index|
|
77
|
+
disassemble_kernel kernel_index
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private def io_registers(input, kernel_index)
|
82
|
+
reg_enum_type = Libevoasm.enum_type(:x64_reg_id)
|
83
|
+
reg_enum_type.to_h.each_with_object([]) do |(k, v), acc|
|
84
|
+
unless k == :none
|
85
|
+
io =
|
86
|
+
if input
|
87
|
+
Libevoasm.program_is_input_reg(self, kernel_index, v)
|
88
|
+
else
|
89
|
+
Libevoasm.program_is_output_reg(self, kernel_index, v)
|
90
|
+
end
|
91
|
+
|
92
|
+
acc << k if io
|
93
|
+
end
|
94
|
+
end
|
17
95
|
end
|
18
96
|
|
19
|
-
|
20
|
-
|
97
|
+
# Gives the input registers of the specified kernel
|
98
|
+
# @param kernel_index [Integer]
|
99
|
+
# @return [Array<Symbol>] input registers
|
100
|
+
def input_registers(kernel_index = 0)
|
101
|
+
io_registers true, kernel_index
|
102
|
+
end
|
103
|
+
|
104
|
+
# Gives the output registers of the specified kernel
|
105
|
+
# @param kernel_index [Integer]
|
106
|
+
# @return [Array<Symbol>] output registers
|
107
|
+
def output_registers(kernel_index = size - 1)
|
108
|
+
io_registers false, kernel_index
|
109
|
+
end
|
110
|
+
|
111
|
+
private def format_disassembly(disasm)
|
112
|
+
disasm.map do |line|
|
113
|
+
"0x#{line[0].to_s 16}:\t#{line[1]}\t#{line[2]}"
|
114
|
+
end.join("\n")
|
115
|
+
end
|
116
|
+
|
117
|
+
# Disassembles the whole program
|
118
|
+
# @param frame [Bool] whether to include the stack frame and
|
119
|
+
# @param format [Bool] whether to format the assembly
|
120
|
+
# @return [String, Array<String>] the formatted assembly as string
|
121
|
+
# if format is set, an array of address, opcode, operands triples otherwise.
|
122
|
+
def disassemble(frame = false, format: false)
|
123
|
+
code_ptr_ptr = FFI::MemoryPointer.new :pointer
|
124
|
+
code_len = Libevoasm.program_get_code self, frame, code_ptr_ptr
|
125
|
+
code_ptr = code_ptr_ptr.read_pointer
|
126
|
+
code = code_ptr.read_string(code_len)
|
127
|
+
|
128
|
+
disasm = X64.disassemble code, code_ptr.address
|
129
|
+
|
130
|
+
if format
|
131
|
+
format_disassembly disasm
|
132
|
+
else
|
133
|
+
disasm
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Visualizes the program and its kernels using Graphviz
|
138
|
+
# @return [GV::Graph]
|
139
|
+
def to_gv
|
140
|
+
require 'gv'
|
141
|
+
|
142
|
+
graph = GV::Graph.open 'g'
|
143
|
+
|
144
|
+
disasms = disassemble_kernels
|
145
|
+
addrs = disasms.map do |disasm|
|
146
|
+
disasm.first&.first
|
147
|
+
end
|
148
|
+
|
149
|
+
size.times do |kernel_index|
|
150
|
+
label = '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">'
|
151
|
+
|
152
|
+
label << '<TR>'
|
153
|
+
label << %Q{<TD COLSPAN="3"><B>Kernel #{kernel_index}</B></TD>}
|
154
|
+
label << '</TR>'
|
155
|
+
|
156
|
+
disasm = disasms[kernel_index]
|
157
|
+
addr = addrs[kernel_index]
|
158
|
+
jmp_addrs = []
|
159
|
+
|
160
|
+
disasm.each do |line|
|
161
|
+
op_str = line[2]
|
162
|
+
|
163
|
+
label << '<TR>'
|
164
|
+
label << %Q{<TD ALIGN="LEFT">0x#{line[0].to_s 16}</TD>}
|
165
|
+
label << %Q{<TD ALIGN="LEFT">#{line[1]}</TD>}
|
166
|
+
|
167
|
+
if op_str =~ /0x(\h+)/
|
168
|
+
jmp_addr = Integer($1, 16)
|
169
|
+
jmp_addrs << jmp_addr
|
170
|
+
port = jmp_addr
|
171
|
+
else
|
172
|
+
port = ''
|
173
|
+
end
|
174
|
+
label << %Q{<TD ALIGN="LEFT" PORT="#{port}">#{op_str}</TD>}
|
175
|
+
label << '</TR>'
|
176
|
+
end
|
177
|
+
label << '</TABLE>'
|
178
|
+
|
179
|
+
node = graph.node addr.to_s,
|
180
|
+
shape: :none,
|
181
|
+
label: graph.html(label)
|
182
|
+
|
183
|
+
succs = [kernel_index + 1, kernel_index + Libevoasm.program_get_jmp_off(self, kernel_index)].select do |succ|
|
184
|
+
succ < size
|
185
|
+
end
|
186
|
+
|
187
|
+
succs.each do |succ|
|
188
|
+
succ_addr = addrs[succ]
|
189
|
+
tail_port =
|
190
|
+
if jmp_addrs.include? succ_addr
|
191
|
+
# Remove, in case we have the same
|
192
|
+
# successor multiple times
|
193
|
+
# only one of which goes through the jump
|
194
|
+
jmp_addrs.delete succ_addr
|
195
|
+
succ_addr.to_s
|
196
|
+
else
|
197
|
+
's'
|
198
|
+
end
|
199
|
+
graph.edge 'e', node, graph.node(succ_addr.to_s), tailport: tail_port, headport: 'n'
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
graph
|
21
204
|
end
|
22
205
|
end
|
23
206
|
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'evoasm/program'
|
2
|
+
|
3
|
+
module Evoasm
|
4
|
+
class Program
|
5
|
+
# Represents one or multiple input/output tuples.
|
6
|
+
class IO < FFI::AutoPointer
|
7
|
+
|
8
|
+
# Maximum arity for tuples
|
9
|
+
MAX_ARITY = 8
|
10
|
+
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
# @!visibility private
|
14
|
+
def self.release(ptr)
|
15
|
+
Libevoasm.program_io_free(ptr)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param tuples [Array] array of input or output tuples
|
19
|
+
def initialize(tuples)
|
20
|
+
if tuples.is_a?(FFI::Pointer)
|
21
|
+
super(tuples)
|
22
|
+
else
|
23
|
+
tuples = tuples
|
24
|
+
arity = determine_arity tuples
|
25
|
+
|
26
|
+
if arity > MAX_ARITY
|
27
|
+
raise ArgumentError, "maximum arity exceeded (#{arity} > #{MAX_ARITY})"
|
28
|
+
end
|
29
|
+
|
30
|
+
flat_tuples = tuples.flatten
|
31
|
+
|
32
|
+
ptr = Libevoasm.program_io_alloc flat_tuples.size
|
33
|
+
load_tuples ptr, flat_tuples, arity
|
34
|
+
|
35
|
+
super(ptr)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Integer] arity of tuples
|
40
|
+
def arity
|
41
|
+
Libevoasm.program_io_get_arity self
|
42
|
+
end
|
43
|
+
|
44
|
+
# @yield [Array] tuple
|
45
|
+
def each
|
46
|
+
return enum_for(:each) unless block_given?
|
47
|
+
size.times do |tuple_index|
|
48
|
+
yield self[tuple_index]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Converts to array of tuples
|
53
|
+
def to_a
|
54
|
+
Array.new(size) do |tuple_index|
|
55
|
+
self[tuple_index]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Integer] the number of input/output values
|
60
|
+
def length
|
61
|
+
Libevoasm.program_io_get_len self
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [Integer] the number of input/output pairs
|
65
|
+
def size
|
66
|
+
length / arity
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param tuple_index [Integer]
|
70
|
+
# @return [Array] returns the tuple at tuple_index
|
71
|
+
def [](tuple_index)
|
72
|
+
absolute_index = arity * tuple_index
|
73
|
+
if arity > 1
|
74
|
+
Array.new(arity) do |value_index|
|
75
|
+
value_at(absolute_index + value_index)
|
76
|
+
end
|
77
|
+
else
|
78
|
+
value_at(absolute_index)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def value_at(index)
|
85
|
+
type = Libevoasm.program_io_get_type self, index
|
86
|
+
case type
|
87
|
+
when :i64
|
88
|
+
Libevoasm.program_io_get_value_i64 self, index
|
89
|
+
when :f64
|
90
|
+
Libevoasm.program_io_get_value_f64 self, index
|
91
|
+
else
|
92
|
+
raise "unknown value type #{type}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def load_tuples(ptr, flat_tuples, arity)
|
97
|
+
var_args = flat_tuples.flat_map do |tuple_value|
|
98
|
+
tuple_type, c_type = value_types tuple_value
|
99
|
+
[:io_val_type, tuple_type, c_type, tuple_value]
|
100
|
+
end
|
101
|
+
|
102
|
+
success = Libevoasm.program_io_init ptr, arity, *var_args
|
103
|
+
|
104
|
+
unless success
|
105
|
+
Libevoasm.program_io_unref ptr
|
106
|
+
raise Error.last
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def value_types(value)
|
111
|
+
case value
|
112
|
+
when Float
|
113
|
+
[:f64, :double]
|
114
|
+
when Integer
|
115
|
+
[:i64, :int64]
|
116
|
+
else
|
117
|
+
raise ArgumentError,
|
118
|
+
"invalid tuple value '#{value}' of type '#{value.class}'"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def determine_arity(tuples)
|
123
|
+
arity = nil
|
124
|
+
tuples.each do |tuple|
|
125
|
+
tuple_arity = Array(tuple).size
|
126
|
+
if arity && arity != tuple_arity
|
127
|
+
raise ArgumentError,
|
128
|
+
"invalid arity for tuple '#{tuple}' (#{tuple_arity} for #{arity})"
|
129
|
+
end
|
130
|
+
arity = tuple_arity
|
131
|
+
end
|
132
|
+
arity || 0
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Represents a program's input
|
137
|
+
class Input < IO
|
138
|
+
end
|
139
|
+
|
140
|
+
# Represents a program's output
|
141
|
+
class Output < IO
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
data/lib/evoasm/test.rb
ADDED
data/lib/evoasm/version.rb
CHANGED
data/lib/evoasm/x64.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'evoasm/libevoasm'
|
2
|
+
require 'evoasm/capstone'
|
3
|
+
require 'evoasm/x64/instruction'
|
4
|
+
require 'evoasm/x64/operand'
|
5
|
+
require 'evoasm/x64/parameters'
|
6
|
+
|
7
|
+
module Evoasm
|
8
|
+
module X64
|
9
|
+
unless Libevoasm.x64_init
|
10
|
+
raise Error.last
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# Disassembles x86-64 machine code
|
15
|
+
# @param assembly [String] assembly
|
16
|
+
# @param address [Integer] optional address to show in the disassembly
|
17
|
+
# @return [String] disassembly
|
18
|
+
def disassemble(assembly, address = nil)
|
19
|
+
Evoasm::Capstone.disassemble_x64 assembly, address
|
20
|
+
end
|
21
|
+
|
22
|
+
# Encodes a x86-64 machine instruction
|
23
|
+
# @param instruction_name [Symbol] the name of the instruction
|
24
|
+
# @param parameters [X64::Parameters, Hash] the instruction parameters
|
25
|
+
# @param buffer [Buffer] the buffer to emit to, if omitted, the encoded instruction is retured as string
|
26
|
+
# @param basic [Boolean] whether to use the basic encoder
|
27
|
+
# @return [String, nil] the encoded machine code as string, or nil if a buffer was provided
|
28
|
+
def encode(instruction_name, parameters, buffer = nil, basic: false)
|
29
|
+
instruction(instruction_name).encode parameters, buffer, basic: basic
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Array<Symbol>] a list of available registers
|
33
|
+
def registers
|
34
|
+
Libevoasm.enum_type(:x64_reg_id).symbols[0..-2]
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Array<Symbol>] the list of supported CPU features (obtained via CPUID)
|
38
|
+
def features
|
39
|
+
feature_enum_type = Libevoasm.enum_type(:x64_feature)
|
40
|
+
arch_info = Libevoasm.get_arch_info :x64
|
41
|
+
features_as_flags = Libevoasm.arch_info_get_features arch_info
|
42
|
+
feature_enum_type.symbol_map.each_with_object({}) do |(k, v), features|
|
43
|
+
next if k == :none
|
44
|
+
supported = (features_as_flags & (1 << v)) != 0
|
45
|
+
features[k] = supported
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Gives an {Instruction} object for the given instruction name.
|
50
|
+
# @param instruction_name [Symbol] instruction name
|
51
|
+
# @return [Instruction]
|
52
|
+
def instruction(instruction_name)
|
53
|
+
Instruction.new Libevoasm.x64_inst(instruction_name), instruction_name
|
54
|
+
end
|
55
|
+
|
56
|
+
# Emits a stack frame for the given ABI
|
57
|
+
# @param abi [Symbol] the ABI to use
|
58
|
+
# @param buffer [Buffer] the buffer to emit to
|
59
|
+
# @return [void]
|
60
|
+
def emit_stack_frame(abi = :sysv, buffer)
|
61
|
+
unless Libevoasm.x64_emit_func_prolog abi, buffer
|
62
|
+
raise Error.last
|
63
|
+
end
|
64
|
+
|
65
|
+
yield
|
66
|
+
|
67
|
+
unless Libevoasm.x64_emit_func_epilog abi, buffer
|
68
|
+
raise Error.last
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Gives a list of instructions
|
73
|
+
# @param reg_types [Array] restrict to given register types (e.g. +:gp+ for general-purpose registers)
|
74
|
+
# @param operand_types [Array<Symbol>] restrict to instructions whose operands are of the specified types
|
75
|
+
# @param useless [Bool] whether to include useless instructions
|
76
|
+
# @param basic [Bool] restrict to instructions supported by the basic encoder
|
77
|
+
# @param features [Array<Symbol>] only give instructions covered by the specified feature set
|
78
|
+
# @return [Array<Symbol>] array of instruction names
|
79
|
+
def instruction_names(*reg_types, operand_types: [:reg, :rm, :imm], useless: false, basic: true, features: nil)
|
80
|
+
inst_id_enum_type = Libevoasm.enum_type(:x64_inst_id)
|
81
|
+
feature_enum_type = Libevoasm.enum_type(:x64_feature)
|
82
|
+
insts_flags_enum_type = Libevoasm.enum_type(:x64_insts_flags)
|
83
|
+
op_type_enum_type = Libevoasm.enum_type(:x64_operand_type)
|
84
|
+
reg_type_enum_type = Libevoasm.enum_type(:x64_reg_type)
|
85
|
+
|
86
|
+
flags = []
|
87
|
+
|
88
|
+
flags << :include_useless if useless
|
89
|
+
flags << :only_basic if basic
|
90
|
+
flags_as_flags = insts_flags_enum_type.flags flags, shift: false
|
91
|
+
|
92
|
+
features_as_flags =
|
93
|
+
if features.nil?
|
94
|
+
arch_info = Libevoasm.get_arch_info :x64
|
95
|
+
Libevoasm.arch_info_get_features arch_info
|
96
|
+
else
|
97
|
+
feature_enum_type.flags features, shift: true
|
98
|
+
end
|
99
|
+
|
100
|
+
op_types_as_flags = op_type_enum_type.flags operand_types, shift: true
|
101
|
+
reg_types_as_flags = reg_type_enum_type.flags reg_types, shift: true
|
102
|
+
|
103
|
+
n_insts = inst_id_enum_type[:none]
|
104
|
+
array = FFI::MemoryPointer.new :int, n_insts
|
105
|
+
len = Libevoasm.x64_insts(flags_as_flags, features_as_flags,
|
106
|
+
op_types_as_flags, reg_types_as_flags, array)
|
107
|
+
|
108
|
+
instruction_ids = array.read_array_of_type(:int, :read_int, len)
|
109
|
+
|
110
|
+
instruction_ids.map { |e| inst_id_enum_type[e] }
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|