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
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
|