evoasm 0.0.2.pre7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gemrelease +2 -0
  3. data/.gitignore +16 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.rake +8 -0
  6. data/Gemfile.rake.lock +51 -0
  7. data/LICENSE.txt +373 -0
  8. data/Makefile +6 -0
  9. data/README.md +43 -0
  10. data/Rakefile +128 -0
  11. data/bin/gdb +2 -0
  12. data/data/tables/README.md +19 -0
  13. data/data/tables/x64.csv +1684 -0
  14. data/data/templates/evoasm-x64.c.erb +319 -0
  15. data/data/templates/evoasm-x64.h.erb +126 -0
  16. data/evoasm.gemspec +30 -0
  17. data/examples/abs.yml +20 -0
  18. data/examples/popcnt.yml +17 -0
  19. data/examples/sym_reg.yml +26 -0
  20. data/exe/evoasm-search +13 -0
  21. data/ext/evoasm_ext/evoasm-alloc.c +145 -0
  22. data/ext/evoasm_ext/evoasm-alloc.h +59 -0
  23. data/ext/evoasm_ext/evoasm-arch.c +44 -0
  24. data/ext/evoasm_ext/evoasm-arch.h +161 -0
  25. data/ext/evoasm_ext/evoasm-bitmap.h +114 -0
  26. data/ext/evoasm_ext/evoasm-buf.c +130 -0
  27. data/ext/evoasm_ext/evoasm-buf.h +47 -0
  28. data/ext/evoasm_ext/evoasm-error.c +31 -0
  29. data/ext/evoasm_ext/evoasm-error.h +75 -0
  30. data/ext/evoasm_ext/evoasm-free-list.c.tmpl +121 -0
  31. data/ext/evoasm_ext/evoasm-free-list.h.tmpl +86 -0
  32. data/ext/evoasm_ext/evoasm-log.c +108 -0
  33. data/ext/evoasm_ext/evoasm-log.h +69 -0
  34. data/ext/evoasm_ext/evoasm-misc.c +23 -0
  35. data/ext/evoasm_ext/evoasm-misc.h +282 -0
  36. data/ext/evoasm_ext/evoasm-param.h +37 -0
  37. data/ext/evoasm_ext/evoasm-search.c +2145 -0
  38. data/ext/evoasm_ext/evoasm-search.h +214 -0
  39. data/ext/evoasm_ext/evoasm-util.h +40 -0
  40. data/ext/evoasm_ext/evoasm-x64.c +275624 -0
  41. data/ext/evoasm_ext/evoasm-x64.h +5436 -0
  42. data/ext/evoasm_ext/evoasm.c +7 -0
  43. data/ext/evoasm_ext/evoasm.h +23 -0
  44. data/ext/evoasm_ext/evoasm_ext.c +1757 -0
  45. data/ext/evoasm_ext/extconf.rb +31 -0
  46. data/lib/evoasm/cli/search.rb +127 -0
  47. data/lib/evoasm/cli.rb +6 -0
  48. data/lib/evoasm/core_ext/array.rb +9 -0
  49. data/lib/evoasm/core_ext/integer.rb +10 -0
  50. data/lib/evoasm/core_ext/kwstruct.rb +13 -0
  51. data/lib/evoasm/core_ext/range.rb +5 -0
  52. data/lib/evoasm/core_ext.rb +1 -0
  53. data/lib/evoasm/error.rb +20 -0
  54. data/lib/evoasm/examples.rb +27 -0
  55. data/lib/evoasm/gen/enum.rb +169 -0
  56. data/lib/evoasm/gen/name_util.rb +80 -0
  57. data/lib/evoasm/gen/state.rb +176 -0
  58. data/lib/evoasm/gen/state_dsl.rb +152 -0
  59. data/lib/evoasm/gen/strio.rb +27 -0
  60. data/lib/evoasm/gen/translator.rb +1102 -0
  61. data/lib/evoasm/gen/version.rb +5 -0
  62. data/lib/evoasm/gen/x64/funcs.rb +495 -0
  63. data/lib/evoasm/gen/x64/inst.rb +781 -0
  64. data/lib/evoasm/gen/x64.rb +237 -0
  65. data/lib/evoasm/gen.rb +8 -0
  66. data/lib/evoasm/program.rb +23 -0
  67. data/lib/evoasm/search.rb +40 -0
  68. data/lib/evoasm/tasks/gen_task.rb +86 -0
  69. data/lib/evoasm/tasks/template_task.rb +52 -0
  70. data/lib/evoasm/version.rb +3 -0
  71. data/lib/evoasm.rb +22 -0
  72. data/test/test_helper.rb +1 -0
  73. data/test/x64/test_helper.rb +19 -0
  74. data/test/x64/x64_test.rb +87 -0
  75. metadata +221 -0
@@ -0,0 +1,1102 @@
1
+ require 'erubis'
2
+ require 'evoasm/gen/strio'
3
+ require 'evoasm/gen/enum'
4
+ require 'evoasm/gen/name_util'
5
+ require 'evoasm/gen/x64'
6
+
7
+ module Evoasm
8
+ module Gen
9
+ class BaseTranslator
10
+ include NameUtil
11
+
12
+ PARAMS_ARG_HELPERS = %i(address_size operand_size disp_size)
13
+ NO_ARCH_HELPERS = %i(log2)
14
+
15
+ def acc_c_type
16
+ name_to_c :bitmap128
17
+ end
18
+
19
+ def arch_c_type
20
+ name_to_c arch
21
+ end
22
+
23
+ def param_val_c_type
24
+ name_to_c 'arch_param_val'
25
+ end
26
+
27
+ def bitmap_c_type
28
+ name_to_c 'bitmap'
29
+ end
30
+
31
+ def local_param?(name)
32
+ name.to_s[0] == '_'
33
+ end
34
+
35
+ def arch_var_name(indep_arch = false)
36
+ "#{indep_arch ? '((evoasm_arch *)' : ''}#{arch}#{indep_arch ? ')' : ''}"
37
+ end
38
+
39
+ def call_to_c(func, args, prefix = nil, eol: false)
40
+ func_name = func.to_s.gsub('?', '_p')
41
+
42
+ if prefix
43
+ args.unshift arch_var_name(Array(prefix).first != arch)
44
+ end
45
+
46
+ "#{name_to_c func_name, prefix}(#{args.join ','})" + (eol ? ';' : '')
47
+ end
48
+
49
+ def params_c_args
50
+ "#{param_val_c_type} *param_vals, "\
51
+ "#{bitmap_c_type} *set_params"
52
+ end
53
+
54
+
55
+ def params_args
56
+ %w(param_vals set_params)
57
+ end
58
+
59
+ def param_to_c(name)
60
+ register_param name.to_sym
61
+ param_name_to_c name
62
+ end
63
+
64
+ def register_param(name)
65
+ return if local_param? name
66
+ main_translator.register_param name
67
+ registered_params << name
68
+ end
69
+
70
+ def helper_to_c(expr)
71
+ if expr.first.is_a?(Array)
72
+ fail expr.inspect unless expr.size == 1
73
+ expr = expr.first
74
+ end
75
+
76
+ name, *args = simplify_helper expr
77
+ case name
78
+ when :eq, :gt, :lt, :gtq, :ltq
79
+ "(#{expr_to_c args[0]} #{cmp_helper_to_c name} #{expr_to_c args[1]})"
80
+ when :if
81
+ "(#{expr_to_c args[0]} ? (#{expr_to_c args[1]}) : #{expr_to_c args[2]})"
82
+ when :neg
83
+ "~(#{expr_to_c args[0]})"
84
+ when :shl
85
+ infix_op_to_c '<<', args
86
+ when :mod
87
+ infix_op_to_c '%', args
88
+ when :div
89
+ infix_op_to_c '/', args
90
+ when :add
91
+ infix_op_to_c '+', args
92
+ when :sub
93
+ infix_op_to_c '-', args
94
+ when :set?
95
+ set_p_to_c(*args)
96
+ when :not
97
+ "!(#{expr_to_c args[0]})"
98
+ when :max, :min
99
+ "#{name.to_s.upcase}(#{args.map { |a| expr_to_c a }.join(', ')})"
100
+ when :and
101
+ infix_op_to_c '&&', args
102
+ when :or
103
+ infix_op_to_c '||', args
104
+ when :in?
105
+ args[1..-1].map { |a| "#{expr_to_c args[0]} == #{expr_to_c a}" }
106
+ .join(" ||\n#{io.indent_str + ' '}")
107
+ else
108
+ if !name.is_a?(Symbol)
109
+ fail unless args.empty?
110
+ expr_to_c name
111
+ else
112
+ call_args = args.map { |a| expr_to_c(a) }
113
+ call_args.concat params_args if PARAMS_ARG_HELPERS.include? name
114
+ if name == :reg_code
115
+ call_args[0] = "(evoasm_#{arch}_reg_id) #{call_args[0]}"
116
+ end
117
+ helper_call_to_c name, call_args
118
+ end
119
+ end
120
+ end
121
+
122
+ def expr_to_c(expr, const_prefix: nil)
123
+ case expr
124
+ when Array
125
+ helper_to_c expr
126
+ when TrueClass
127
+ 'true'
128
+ when FalseClass
129
+ 'false'
130
+ when Numeric
131
+ expr
132
+ when Symbol, String
133
+ s = expr.to_s
134
+ if s != s.upcase
135
+ get_to_c s
136
+ else
137
+ if X64::REGISTER_NAMES.include?(s.to_sym)
138
+ const_prefix = [arch, 'reg']
139
+ elsif s =~ /^INT\d+_(MAX|MIN)$/
140
+ const_prefix = nil
141
+ end
142
+
143
+ name_to_c s, const_prefix, const: true
144
+ end
145
+ else
146
+ fail "invalid expression #{expr.inspect}"
147
+ end
148
+ end
149
+
150
+ def func_prototype_to_c(name, func_params = [], static: true)
151
+ func_name = name_to_c name, arch
152
+
153
+ func_params_c =
154
+ if func_params.empty?
155
+ ''
156
+ else
157
+ func_params.map do |param_name, type|
158
+ "#{type} #{param_name}"
159
+ end.join(', ').prepend ', '
160
+ end
161
+ "#{static ? 'static ' : ''}evoasm_success\n#{func_name}(#{arch_c_type} *#{arch_var_name},"\
162
+ " #{params_c_args}#{func_params_c})"
163
+ end
164
+ end
165
+
166
+ class FuncTranslator < BaseTranslator
167
+ INST_STATE_ID_MIN = 32
168
+ INST_STATE_ID_MAX = 2000
169
+
170
+ attr_reader :inst, :registered_params, :root_state
171
+ attr_reader :main_translator, :id_map
172
+ attr_reader :arch, :io, :param_domains
173
+
174
+ def initialize(arch, main_translator)
175
+ @arch = arch
176
+ @main_translator = main_translator
177
+ @id = INST_STATE_ID_MAX
178
+ @id_map = Hash.new { |h, k| h[k] = (@id += 1) }
179
+ @registered_params = Set.new
180
+ @param_domains = {}
181
+ end
182
+
183
+ def with_io(io)
184
+ @io = io
185
+ yield
186
+ @io = nil
187
+ end
188
+
189
+ def pref_func_name(id)
190
+ "prefs_#{id}"
191
+ end
192
+
193
+ def inst_id_c_type
194
+ name_to_c :inst_id
195
+ end
196
+
197
+ def called_func_name(func, id)
198
+ attrs = func.each_pair.map { |k, v| [k, v].join('_') }.flatten.join('__')
199
+ "#{func.class.name.split('::').last.downcase}_#{attrs}_#{id}"
200
+ end
201
+
202
+ def emit_func(name, root_state, func_params = [], local_acc: true, static: true)
203
+ io.puts func_prototype_to_c(name, func_params, static: static), eol: ' {'
204
+
205
+ io.indent do
206
+ emit_func_prolog root_state, local_acc
207
+ emit_state root_state
208
+ emit_func_epilog local_acc
209
+ end
210
+
211
+
212
+ io.puts '}'
213
+ io.puts
214
+ end
215
+
216
+ def emit_acc_ary_copy(back_copy = false)
217
+ var_name = 'acc'
218
+ src = "#{arch_var_name arch_indep: true}->#{var_name}"
219
+ dst = var_name
220
+
221
+ dst, src = src, dst if back_copy
222
+ io.puts "#{dst} = #{src};"
223
+ end
224
+
225
+ def emit_func_prolog(root_state, acc)
226
+ local_params = root_state.local_params
227
+ unless local_params.empty?
228
+ io.puts "#{param_val_c_type} #{local_params.join ', '};"
229
+ local_params.each do |param|
230
+ io.puts "(void) #{param};"
231
+ end
232
+ end
233
+
234
+ io.puts 'bool retval = true;'
235
+
236
+ if acc
237
+ io.puts "#{acc_c_type} acc;"
238
+ emit_acc_ary_copy
239
+ end
240
+ end
241
+
242
+ def error_data_field_to_c(field_name)
243
+ "#{arch_var_name arch_indep: true}->error_data.#{field_name}"
244
+ end
245
+
246
+ def emit_error(state, code, msg, reg = nil, param = nil)
247
+ reg_c_val =
248
+ if reg
249
+ reg_name_to_c reg
250
+ else
251
+ "(uint8_t) -1"
252
+ end
253
+ param_c_val =
254
+ if param
255
+ param_to_c param
256
+ else
257
+ "(uint8_t) -1"
258
+ end
259
+
260
+ io.write <<-EOL
261
+ evoasm_arch_error_data error_data = {
262
+ .reg = #{reg_c_val},
263
+ .param = #{param_c_val},
264
+ .arch = #{arch_var_name arch_indep: true},
265
+ };
266
+ EOL
267
+
268
+ io.puts %Q{evoasm_set_error(EVOASM_ERROR_TYPE_ARCH, #{error_code_to_c code}, &error_data, "#{msg}");}
269
+ io.puts 'retval = false;'
270
+ end
271
+
272
+ def emit_func_epilog(acc)
273
+ io.indent 0 do
274
+ io.puts "exit:"
275
+ end
276
+ emit_acc_ary_copy true if acc
277
+ io.puts "return retval;"
278
+
279
+ io.indent 0 do
280
+ io.puts "error:"
281
+ end
282
+
283
+ io.puts 'retval = false;'
284
+ io.puts 'goto exit;'
285
+ end
286
+
287
+ def emit_state(state)
288
+ fail if state.nil?
289
+
290
+ unemitted_states = []
291
+
292
+ fail if state.ret? && !state.terminal?
293
+
294
+ emit_body state, unemitted_states
295
+
296
+ unemitted_states.each do |unemitted_state|
297
+ emit_state unemitted_state
298
+ end
299
+ end
300
+
301
+ def emit_body(state, unemitted_states, inlined = false)
302
+ fail state.actions.inspect unless deterministic?(state)
303
+ io.puts '/* begin inlined */' if inlined
304
+
305
+ emit_label state unless inlined
306
+
307
+ actions = state.actions.dup.reverse
308
+ emit_actions state, actions, unemitted_states
309
+
310
+ emit_ret state if state.ret?
311
+
312
+ emit_transitions(state, unemitted_states)
313
+
314
+ io.puts '/* end inlined */' if inlined
315
+ end
316
+
317
+ def emit_comment(state)
318
+ io.puts "/* #{state.comment} (#{state.object_id}) */" if state.comment
319
+ end
320
+
321
+ def emit_label(state)
322
+ io.indent 0 do
323
+ io.puts "#{state_label state}:;"
324
+ end
325
+ end
326
+
327
+ def has_else?(state)
328
+ state.children.any? { |_, cond| cond == [:else] }
329
+ end
330
+
331
+ def emit_ret(state)
332
+ io.puts "goto exit;"
333
+ end
334
+
335
+ def state_label(state, id = nil)
336
+ "L#{id || id_map[state]}"
337
+ end
338
+
339
+ def emit_call(state, func)
340
+ id = main_translator.request_func_call func, self
341
+
342
+ func_call = call_to_c called_func_name(func, id),
343
+ [*params_args, inst_name_to_c(inst), '&acc'],
344
+ arch_prefix
345
+
346
+ io.puts "if(!#{func_call}){goto error;}"
347
+ end
348
+
349
+ def emit_goto_transition(child)
350
+ io.puts "goto #{state_label child};"
351
+ end
352
+
353
+ def emit_transitions(state, unemitted_states, &block)
354
+ state
355
+ .children
356
+ .sort_by { |_, _, attrs| attrs[:priority] }
357
+ .each do |child, expr|
358
+ emit_cond expr do
359
+ if inlineable?(child)
360
+ block[] if block
361
+ emit_body(child, unemitted_states, true)
362
+ true
363
+ else
364
+ unemitted_states << child unless id_map.key?(child)
365
+ block[] if block
366
+ emit_goto_transition(child)
367
+ false
368
+ end
369
+ end
370
+ end
371
+
372
+ fail 'missing else branch' if can_get_stuck?(state)
373
+ end
374
+
375
+ def can_get_stuck?(state)
376
+ return false if state.ret?
377
+ return false if has_else? state
378
+
379
+ fail state.actions.inspect if state.children.empty?
380
+
381
+ return false if state.children.any? do |_child, cond|
382
+ cond.nil? || cond == [true]
383
+ end
384
+
385
+ true
386
+ end
387
+
388
+ def helper_call_to_c(name, args)
389
+ prefix =
390
+ if NO_ARCH_HELPERS.include?(name)
391
+ nil
392
+ else
393
+ arch_prefix
394
+ end
395
+
396
+ call_to_c name, args, prefix
397
+ end
398
+
399
+ def simplify_helper(helper)
400
+ simplified_helper = simplify_helper_ helper
401
+ return simplified_helper if simplified_helper == helper
402
+ simplify_helper simplified_helper
403
+ end
404
+
405
+ def simplify_helper_(helper)
406
+ name, *args = helper
407
+ case name
408
+ when :neq
409
+ [:not, [:eq, *args]]
410
+ when :false?
411
+ [:eq, *args, 0]
412
+ when :true?
413
+ [:not, [:false?, *args]]
414
+ when :unset?
415
+ [:not, [:set?, args[0]]]
416
+ when :in?
417
+ [:or, *args[1..-1].map { |arg| [:eq, args.first, arg] }]
418
+ when :not_in?
419
+ [:not, [:in?, *args]]
420
+ else
421
+ helper
422
+ end
423
+ end
424
+
425
+ def emit_cond(cond, else_if: false, &block)
426
+ cond_str =
427
+ if cond.nil? || cond == true
428
+ ''
429
+ elsif cond[0] == :else
430
+ 'else '
431
+ else
432
+ "#{else_if ? 'else ' : ''}if(#{expr_to_c cond})"
433
+ end
434
+
435
+ emit_c_block cond_str, &block
436
+ end
437
+
438
+ def emit_log(_state, level, msg, *exprs)
439
+ expr_part =
440
+ if !exprs.empty?
441
+ ", #{exprs.map { |expr| "(#{param_val_c_type}) #{expr_to_c expr}" }.join(', ')}"
442
+ else
443
+ ''
444
+ end
445
+ msg = msg.gsub('%', '%" EVOASM_PARAM_VAL_FORMAT "')
446
+ io.puts %[evoasm_#{level}("#{msg}" #{expr_part});]
447
+ end
448
+
449
+ def emit_assert(_state, *expr)
450
+ io.puts "assert(#{expr_to_c expr});"
451
+ end
452
+
453
+ def set_p_to_c(key, eol: false)
454
+ call_to_c 'bitmap_get',
455
+ ["(#{bitmap_c_type} *) set_params", param_to_c(key)],
456
+ eol: eol
457
+ end
458
+
459
+ def get_to_c(key, eol: false)
460
+ if local_param? key
461
+ key.to_s
462
+ else
463
+ "param_vals[#{param_to_c(key)}]" + (eol ? ';' : '')
464
+ end
465
+ end
466
+
467
+ def emit_set(_state, key, value, c_value: false)
468
+ fail "setting non-local param '#{key}' is not allowed" unless local_param? key
469
+
470
+ c_value =
471
+ if c_value
472
+ value
473
+ else
474
+ expr_to_c value
475
+ end
476
+
477
+ io.puts "#{key} = #{c_value};"
478
+ end
479
+
480
+ def merge_params(params)
481
+ params.each do |param|
482
+ register_param param
483
+ end
484
+ end
485
+
486
+ def cmp_helper_to_c(name)
487
+ case name
488
+ when :eq then '=='
489
+ when :gt then '>'
490
+ when :lt then '<'
491
+ when :gtq then '>='
492
+ when :ltq then '<='
493
+ else
494
+ fail
495
+ end
496
+ end
497
+
498
+ def infix_op_to_c(op, args)
499
+ "(#{args.map { |a| expr_to_c a }.join(" #{op} ")})"
500
+ end
501
+
502
+ def emit_actions(state, actions, _unemitted_states)
503
+ io.puts '/* actions */'
504
+ until actions.empty?
505
+ name, args = actions.last
506
+ actions.pop
507
+ send :"emit_#{name}", state, *args
508
+ end
509
+ end
510
+
511
+ def deterministic?(state)
512
+ n_children = state.children.size
513
+
514
+ n_children <= 1 ||
515
+ (n_children == 2 && has_else?(state))
516
+ end
517
+
518
+ def inlineable?(state)
519
+ state.parents.size == 1 &&
520
+ deterministic?(state.parents.first)
521
+ end
522
+
523
+ def emit_c_block(code = nil, &block)
524
+ io.puts "#{code}{"
525
+ io.indent do
526
+ block[]
527
+ end
528
+ io.puts '}'
529
+ end
530
+
531
+ def emit_unordered_writes(state, param_name, writes)
532
+ if writes.size > 1
533
+ id, table_size = main_translator.request_pref_func writes, self
534
+ func_name = pref_func_name(id)
535
+
536
+ call_c = call_to_c(func_name,
537
+ [*params_args, param_name_to_c(param_name)],
538
+ arch_prefix)
539
+
540
+ io.puts call_c, eol: ';'
541
+
542
+ register_param param_name
543
+ @param_domains[param_name] = (0..table_size - 1)
544
+ elsif writes.size > 0
545
+ cond, write_args = writes.first
546
+ emit_cond cond do
547
+ emit_write(state, *write_args)
548
+ end
549
+ end
550
+ end
551
+
552
+ def emit_read_access(state, op)
553
+ call = access_call_to_c 'read', op, "#{arch_var_name(true)}->acc",
554
+ [inst && inst_name_to_c(inst) || 'inst']
555
+
556
+ #emit_c_block "if(!#{call})" do
557
+ # emit_exit error: true
558
+ #end
559
+ io.puts call, eol: ';'
560
+ end
561
+
562
+ def access_call_to_c(name, op, acc = 'acc', params = [], eol: false)
563
+ call_to_c("#{name}_access",
564
+ [
565
+ "(#{bitmap_c_type} *) &#{acc}",
566
+ "(#{regs.c_type}) #{expr_to_c(op)}",
567
+ *params
568
+ ],
569
+ indep_arch_prefix,
570
+ eol: eol)
571
+ end
572
+
573
+ def emit_write_access(_state, op)
574
+ io.puts access_call_to_c('write', op, eol: true)
575
+ end
576
+
577
+ def emit_undefined_access(_state, op)
578
+ io.puts access_call_to_c('undefined', op, eol: true)
579
+ end
580
+
581
+ def write_to_c(value, size)
582
+ if size.is_a?(Array) && value.is_a?(Array)
583
+ value_c, size_c = value.reverse.zip(size.reverse).inject(['0', 0]) do |(v_, s_), (v, s)|
584
+ [v_ + " | ((#{expr_to_c v} & ((1 << #{s}) - 1)) << #{s_})", s_ + s]
585
+ end
586
+ else
587
+ value_c =
588
+ case value
589
+ when Integer
590
+ '0x' + value.to_s(16)
591
+ else
592
+ expr_to_c value
593
+ end
594
+
595
+ size_c = expr_to_c size
596
+ end
597
+
598
+ call_to_c "write#{size_c}", [value_c], indep_arch_prefix, eol: true
599
+ end
600
+
601
+ def emit_write(_state, value, size)
602
+ io.puts write_to_c(value, size)
603
+ end
604
+
605
+ def emit_access(state, op, access)
606
+ #access.each do |mode|
607
+ # case mode
608
+ # when :r
609
+ # emit_read_access state, op
610
+ # when :w
611
+ # emit_write_access state, op
612
+ # when :u
613
+ # emit_undefined_access state, op
614
+ # else
615
+ # fail "unexpected access mode '#{rw.inspect}'"
616
+ # end
617
+ #end
618
+ end
619
+
620
+ def emit_inst_func(io, inst)
621
+ @inst = inst
622
+ with_io io do
623
+ emit_func inst.name, inst.root_state, static: false
624
+ end
625
+ end
626
+
627
+ def emit_called_func(io, func, id)
628
+ with_io io do
629
+ emit_func(called_func_name(func, id),
630
+ func.root_state,
631
+ {'inst' => inst_id_c_type, 'acc' => "#{acc_c_type} *"},
632
+ local_acc: false)
633
+ end
634
+ end
635
+
636
+ def emit_pref_func(io, writes, id)
637
+ with_io io do
638
+ table_var_name, _table_size = main_translator.request_permutation_table writes.size
639
+ func_name = name_to_c pref_func_name(id), arch_prefix
640
+
641
+ emit_c_block "static void\n#{func_name}(#{arch_c_type} *#{arch_var_name},"\
642
+ " #{params_c_args}, #{main_translator.param_names.c_type} order)" do
643
+ io.puts 'int i;'
644
+ emit_c_block "for(i = 0; i < #{writes.size}; i++)" do
645
+ emit_c_block "switch(#{table_var_name}[param_vals[order]][i])" do
646
+ writes.each_with_index do |write, index|
647
+ cond, write_args = write
648
+ emit_c_block "case #{index}:" do
649
+ emit_cond cond do
650
+ io.puts write_to_c(*write_args)
651
+ end
652
+ io.puts 'break;'
653
+ end
654
+ end
655
+ io.puts "default: evoasm_assert_not_reached();"
656
+ end
657
+ end
658
+ end
659
+ end
660
+ end
661
+ end
662
+
663
+ class Translator < BaseTranslator
664
+ attr_reader :params, :param_names
665
+ attr_reader :id_map, :arch
666
+ attr_reader :options
667
+ attr_reader :features, :inst_flags
668
+ attr_reader :reg_names, :exceptions
669
+ attr_reader :reg_types, :operand_types
670
+ attr_reader :bit_masks
671
+ attr_reader :insts, :regs
672
+ attr_reader :registered_param_domains
673
+
674
+ STATIC_PARAMS = %i(reg0 reg1 reg2 reg3 reg4 imm operand_size address_size)
675
+ PARAM_ALIASES = { imm0: :imm }
676
+
677
+ def initialize(arch, insts, options = {})
678
+ @arch = arch
679
+ @insts = insts
680
+ @pref_funcs = {}
681
+ @called_funcs = {}
682
+ @options = options
683
+ @registered_param_domains = Set.new
684
+
685
+ load_enums
686
+ end
687
+
688
+ def self.target_filename(arch, header: false)
689
+ "evoasm-#{arch}.#{header ? 'h' : 'c'}"
690
+ end
691
+
692
+ def self.template_path(arch, header: false)
693
+ File.join Evoasm.data, 'templates', "#{target_filename(arch, header: header)}.erb"
694
+ end
695
+
696
+ def translate!(&block)
697
+ case arch
698
+ when :x64
699
+ translate_x64(&block)
700
+ else
701
+ fail "unsupported architecture #{@arch}"
702
+ end
703
+ end
704
+
705
+ def main_translator
706
+ self
707
+ end
708
+
709
+ def register_param(name)
710
+ param_names.add name, PARAM_ALIASES[name]
711
+ end
712
+
713
+ def request_pref_func(writes, translator)
714
+ _, table_size = request_permutation_table(writes.size)
715
+ [request(@pref_funcs, writes, translator), table_size]
716
+ end
717
+
718
+ def request_func_call(func, translator)
719
+ request @called_funcs, func, translator
720
+ end
721
+
722
+ def request_permutation_table(n)
723
+ @permutation_tables ||= Hash.new { |h, k| h[k] = (0...k).to_a.permutation }
724
+ [permutation_table_var_name(n), @permutation_tables[n].size]
725
+ end
726
+
727
+ private
728
+ def load_enums
729
+ @param_names = Enum.new :param_id, STATIC_PARAMS, prefix: arch
730
+
731
+ case arch
732
+ when :x64
733
+ @features = Enum.new :feature, prefix: arch, flags: true
734
+ @inst_flags = Enum.new :inst_flag, prefix: arch, flags: true
735
+ @exceptions = Enum.new :exception_id, prefix: arch
736
+ @reg_types = Enum.new :reg_type, Evoasm::Gen::X64::REGISTERS.keys, prefix: arch
737
+ @operand_types = Enum.new :operand_type, Evoasm::Gen::X64::Inst::OPERAND_TYPES, prefix: arch
738
+ @reg_names = Enum.new :reg_id, Evoasm::Gen::X64::REGISTER_NAMES, prefix: arch
739
+ @bit_masks = Enum.new :bit_mask, %i(rest 64_127 32_63 0_31), prefix: arch, flags: true
740
+ end
741
+ end
742
+
743
+ def register_param_domain(domain)
744
+ @registered_param_domains << domain
745
+ end
746
+
747
+ def translate_x64(&block)
748
+ translate_x64_c(&block)
749
+
750
+ # NOTE: must be done after
751
+ # translating C file
752
+ # as we are collecting information
753
+ # in the translation process
754
+ translate_x64_h(&block)
755
+ end
756
+
757
+ def translate_x64_h(&block)
758
+ target_filename = self.class.target_filename(arch, header: true)
759
+ template_path = self.class.template_path(arch, header: true)
760
+
761
+ renderer = Erubis::Eruby.new(File.read(template_path))
762
+ block[target_filename, renderer.result(binding)]
763
+ end
764
+
765
+ def translate_x64_c(&block)
766
+ target_filename = self.class.target_filename(arch)
767
+ template_path = self.class.template_path(arch)
768
+
769
+ # NOTE: keep in correct order
770
+ inst_funcs = inst_funcs_to_c
771
+ pref_funcs = pref_funcs_to_c
772
+ permutation_tables = permutation_tables_to_c
773
+ called_funcs = called_funcs_to_c
774
+ insts_c = insts_to_c
775
+ inst_operands = inst_operands_to_c
776
+ inst_params = inst_params_to_c
777
+ param_domains = param_domains_to_c
778
+
779
+ renderer = Erubis::Eruby.new(File.read(template_path))
780
+ block[target_filename, renderer.result(binding)]
781
+ end
782
+
783
+ def operand_c_type
784
+ name_to_c :operand, arch_prefix
785
+ end
786
+
787
+ def param_c_type
788
+ name_to_c :arch_param
789
+ end
790
+
791
+ def inst_params_var_name(inst)
792
+ "params_#{inst.name}"
793
+ end
794
+
795
+ def insts_var_name
796
+ "_evoasm_#{arch}_insts"
797
+ end
798
+
799
+ def inst_operands_var_name(inst)
800
+ "operands_#{inst.name}"
801
+ end
802
+
803
+ def inst_param_domains_var_name(inst)
804
+ "domains_#{inst.name}"
805
+ end
806
+
807
+ def param_domain_var_name(domain)
808
+ case domain
809
+ when Range
810
+ "param_domain__#{domain.begin.to_s.tr('-', 'm')}_#{domain.end}"
811
+ when Array
812
+ "param_domain_enum__#{domain.join '_'}"
813
+ else
814
+ fail "unexpected domain type #{domain.class} (#{domain.inspect})"
815
+ end
816
+ end
817
+
818
+ def permutation_table_var_name(n)
819
+ "permutations#{n}"
820
+ end
821
+
822
+ def inst_encode_func_name(inst)
823
+ name_to_c inst.name, arch_prefix
824
+ end
825
+
826
+ def inst_funcs_to_c(io = StrIO.new)
827
+ @inst_translators = insts.map do |inst|
828
+ @features.add_all inst.features
829
+ @inst_flags.add_all inst.flags
830
+ @exceptions.add_all inst.exceptions
831
+
832
+ inst_translator = FuncTranslator.new arch, self
833
+ inst_translator.emit_inst_func io, inst
834
+
835
+ inst_translator
836
+ end
837
+
838
+ io.string
839
+ end
840
+
841
+ def permutation_tables_to_c(io = StrIO.new)
842
+ Hash(@permutation_tables).each do |n, perms|
843
+ io.puts "static int #{permutation_table_var_name n}"\
844
+ "[#{perms.size}][#{perms.first.size}] = {"
845
+
846
+ perms.each do |perm|
847
+ io.puts " {#{perm.join ', '}},"
848
+ end
849
+ io.puts '};'
850
+ io.puts
851
+ end
852
+
853
+ io.string
854
+ end
855
+
856
+ def called_funcs_to_c(io = StrIO.new)
857
+ @called_funcs.each do |func, (id, translators)|
858
+ func_translator = FuncTranslator.new arch, self
859
+ func_translator.emit_called_func io, func, id
860
+
861
+ translators.each do |translator|
862
+ translator.merge_params func_translator.registered_params
863
+ end
864
+ end
865
+
866
+ io.string
867
+ end
868
+
869
+ def inst_to_c(io, inst, params)
870
+ io.puts '{'
871
+ io.indent do
872
+ io.puts '{'
873
+ io.indent do
874
+ io.puts inst_name_to_c(inst), eol: ','
875
+ io.puts params.size, eol: ','
876
+ if params.empty?
877
+ io.puts "NULL,"
878
+ else
879
+ io.puts "(#{param_c_type} *)" + inst_params_var_name(inst), eol: ','
880
+ end
881
+ io.puts '(evoasm_inst_encode_func)' + inst_encode_func_name(inst), eol: ','
882
+ end
883
+ io.puts '},'
884
+
885
+ io.puts "#{features_bitmap(inst)}ull", eol: ','
886
+ if inst.operands.empty?
887
+ io.puts 'NULL,'
888
+ else
889
+ io.puts "(#{operand_c_type} *)#{inst_operands_var_name inst}", eol: ','
890
+ end
891
+ io.puts inst.operands.size, eol: ','
892
+ io.puts exceptions_bitmap(inst), eol: ','
893
+ io.puts inst_flags_to_c(inst)
894
+ end
895
+ io.puts '},'
896
+ end
897
+
898
+ def insts_to_c(io = StrIO.new)
899
+ io.puts "static const evoasm_x64_inst #{insts_var_name}[] = {"
900
+ @inst_translators.each do |translator|
901
+ inst_to_c io, translator.inst, translator.registered_params
902
+ end
903
+ io.puts '};'
904
+
905
+ io.string
906
+ end
907
+
908
+ def inst_param_to_c(io, inst, params, param_domains)
909
+ if !params.empty?
910
+ io.puts "static const #{param_c_type} #{inst_params_var_name inst}[] = {"
911
+ io.indent do
912
+ params.each do |param|
913
+ next if local_param? param
914
+
915
+ param_domain = param_domains[param] || inst.param_domain(param)
916
+ register_param_domain param_domain
917
+
918
+ io.puts '{'
919
+ io.indent do
920
+ io.puts param_name_to_c(param), eol: ','
921
+ io.puts '(evoasm_domain *) &' + param_domain_var_name(param_domain)
922
+ end
923
+ io.puts '},'
924
+ end
925
+ end
926
+ io.puts '};'
927
+ io.puts
928
+ end
929
+ end
930
+
931
+ def inst_params_to_c(io = StrIO.new)
932
+ @inst_translators.each do |translator|
933
+ inst_param_to_c io, translator.inst, translator.registered_params, translator.param_domains
934
+ end
935
+
936
+ io.string
937
+ end
938
+
939
+ def inst_operand_to_c(translator, op, io = StrIO.new, eol:)
940
+ io.puts '{'
941
+ io.indent do
942
+ io.puts op.access.include?(:r) ? '1' : '0', eol: ','
943
+ io.puts op.access.include?(:w) ? '1' : '0', eol: ','
944
+ io.puts op.access.include?(:u) ? '1' : '0', eol: ','
945
+ io.puts op.access.include?(:c) ? '1' : '0', eol: ','
946
+ io.puts op.implicit? ? '1' : '0', eol: ','
947
+
948
+ params = translator.registered_params.reject{|p| local_param? p}
949
+ if op.param
950
+ param_idx = params.index(op.param) or \
951
+ raise "param #{op.param} not found in #{translator.params.inspect}" \
952
+ " (#{translator.inst.mnem}/#{translator.inst.index})"
953
+
954
+ io.puts param_idx, eol: ','
955
+ else
956
+ io.puts params.size, eol: ','
957
+ end
958
+
959
+ io.puts operand_type_to_c(op.type), eol: ','
960
+
961
+ if op.size
962
+ io.puts operand_size_to_c(op.size), eol: ','
963
+ else
964
+ io.puts 'EVOASM_N_OPERAND_SIZES', eol: ','
965
+ end
966
+
967
+ if op.reg
968
+ io.puts reg_name_to_c(op.reg), eol: ','
969
+ else
970
+ io.puts reg_names.n_elem_to_c, eol: ','
971
+ end
972
+
973
+ if op.reg_type
974
+ io.puts reg_type_to_c(op.reg_type), eol: ','
975
+ else
976
+ io.puts reg_types.n_elem_to_c, eol: ','
977
+ end
978
+
979
+ if op.accessed_bits.key? :w
980
+ io.puts bit_mask_to_c(op.accessed_bits[:w])
981
+ else
982
+ io.puts bit_masks.all_to_c
983
+ end
984
+ end
985
+ io.puts '}', eol: eol
986
+ end
987
+
988
+ def inst_operands_to_c(io = StrIO.new)
989
+ @inst_translators.each do |translator|
990
+ if !translator.inst.operands.empty?
991
+ io.puts "static const #{operand_c_type} #{inst_operands_var_name translator.inst}[] = {"
992
+ io.indent do
993
+ translator.inst.operands.each do |op|
994
+ inst_operand_to_c(translator, op, io, eol: ',')
995
+ end
996
+ end
997
+ io.puts '};'
998
+ io.puts
999
+ end
1000
+ end
1001
+
1002
+ io.string
1003
+ end
1004
+
1005
+ ENUM_MAX_LENGTH = 32
1006
+ def param_domain_to_c(io, domain, index)
1007
+ domain_c =
1008
+ case domain
1009
+ when (:INT64_MIN..:INT64_MAX)
1010
+ "{EVOASM_DOMAIN_TYPE_INTERVAL64, #{index}, #{0}, #{0}}"
1011
+ when Range
1012
+ min_c = expr_to_c domain.begin
1013
+ max_c = expr_to_c domain.end
1014
+ "{EVOASM_DOMAIN_TYPE_INTERVAL, #{index}, #{min_c}, #{max_c}}"
1015
+ when Array
1016
+ if domain.size > ENUM_MAX_LENGTH
1017
+ raise 'enum exceeds maximal enum length of'
1018
+ end
1019
+ values_c = "#{domain.map { |expr| expr_to_c expr }.join ', '}"
1020
+ "{EVOASM_DOMAIN_TYPE_ENUM, #{index}, #{domain.length}, {#{values_c}}}"
1021
+ end
1022
+
1023
+ domain_c_type =
1024
+ case domain
1025
+ when Range
1026
+ 'evoasm_interval'
1027
+ when Array
1028
+ "evoasm_enum#{domain.size}"
1029
+ end
1030
+ io.puts "static const #{domain_c_type} #{param_domain_var_name domain} = #{domain_c};"
1031
+ end
1032
+
1033
+ def param_domains_to_c(io = StrIO.new)
1034
+ registered_param_domains.each_with_index do |domain, index|
1035
+ param_domain_to_c io, domain, index
1036
+ end
1037
+
1038
+ io.puts "const uint16_t evoasm_n_domains = #{registered_param_domains.size};"
1039
+
1040
+ io.string
1041
+ end
1042
+
1043
+ def request(hash, key, translator)
1044
+ id, translators = hash[key]
1045
+ if id.nil?
1046
+ id = hash.size
1047
+ translators = []
1048
+
1049
+ hash[key] = [id, translators]
1050
+ end
1051
+
1052
+ translators << translator
1053
+ id
1054
+ end
1055
+
1056
+ def pref_funcs_to_c(io = StrIO.new)
1057
+ @pref_funcs.each do |writes, (id, translators)|
1058
+ func_translator = FuncTranslator.new arch, self
1059
+ func_translator.emit_pref_func io, writes, id
1060
+
1061
+ translators.each do |translator|
1062
+ translator.merge_params func_translator.registered_params
1063
+ end
1064
+ end
1065
+
1066
+ io.string
1067
+ end
1068
+
1069
+ def inst_flags_to_c(inst)
1070
+ if inst.flags.empty?
1071
+ "0"
1072
+ else
1073
+ inst.flags.map { |flag| inst_flag_to_c flag }
1074
+ .join ' | '
1075
+ end
1076
+ end
1077
+
1078
+ def features_bitmap(inst)
1079
+ bitmap(features) do |flag, index|
1080
+ inst.features.include?(flag)
1081
+ end
1082
+ end
1083
+
1084
+ def exceptions_bitmap(inst)
1085
+ bitmap(exceptions) do |flag, index|
1086
+ inst.exceptions.include?(flag)
1087
+ end
1088
+ end
1089
+
1090
+ def bitmap(enum, &block)
1091
+ enum.keys.each_with_index.inject(0) do |acc, (flag, index)|
1092
+ if block[flag, index]
1093
+ acc | (1 << index)
1094
+ else
1095
+ acc
1096
+ end
1097
+ end
1098
+ end
1099
+
1100
+ end
1101
+ end
1102
+ end