asmjit 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c624450e0bd1c3f5275f6bab3417f3a147f90efb595c26ca58ae93dd6828aa74
4
- data.tar.gz: 36fab728b2acc6bcca18b8fc415f4e7aca02ed919914f162242bba4f397533a3
3
+ metadata.gz: bcda0d9ba5d87e102ebf2fabf636aa07c3c8b24eca8c66c740fcb8700cd769cb
4
+ data.tar.gz: 4ecec0d43302ffce529cc54ed2a0c46d88035a589897b8a2ceaf3b022dc017a9
5
5
  SHA512:
6
- metadata.gz: 0ff819f0ecbc9750048a3ac529275ee49846b074c30097b182553a99733d149aa14096cf191e6ce50856a0d26a242c020c715801fab1442e2fc9666991106091
7
- data.tar.gz: 62e66a1ab2106832491749cef583d5248649eaf8c77bd29649d4afd39f2f00857a9a5e04f3fe192b38580fa57133b92196e733b3d3b077cf59dc61f2b48e2fc3
6
+ metadata.gz: 1e66beaefdfaa62ff6d9b27a3b984192cc3a7c6906f77ea96a3de303c7e342196b0aef7cd1f00596883720b490403bed9bd9e09ce526fca2c11e1700c9ec1fc3
7
+ data.tar.gz: b649c8f79ab822fb77beb840be1a8174fd71ba7e131320d80d87e8ae79539a18095311eb80492a0901182f4a4aa55c4496e3afd6254514049409829222e9291c
data/Gemfile.lock CHANGED
@@ -1,11 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- asmjit (0.1.0)
4
+ asmjit (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ hatstone (1.0.0)
9
10
  minitest (5.15.0)
10
11
  rake (13.0.6)
11
12
  rake-compiler (1.2.0)
@@ -16,6 +17,7 @@ PLATFORMS
16
17
 
17
18
  DEPENDENCIES
18
19
  asmjit!
20
+ hatstone
19
21
  minitest (~> 5.0)
20
22
  rake (~> 13.0)
21
23
  rake-compiler
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
- # Asmjit
1
+ # AsmJit Ruby
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/asmjit`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Ruby bindings for [AsmJit](https://asmjit.com/), a lightweight library for machine code generation.
6
4
 
7
5
  ## Installation
8
6
 
@@ -24,9 +22,12 @@ Or install it yourself as:
24
22
 
25
23
  ```
26
24
  f = AsmJIT.assemble do |a|
27
- a.mov(:eax, 1)
28
- a.ret()
29
- end.to_func
25
+ a.mov :eax, 123
26
+ a.ret
27
+ end.to_fiddle
28
+
29
+ f.call
30
+ # => 123
30
31
  ```
31
32
 
32
33
  ## Development
data/asmjit.gemspec CHANGED
@@ -29,9 +29,5 @@ Gem::Specification.new do |spec|
29
29
  spec.require_paths = ["lib"]
30
30
  spec.extensions = ["ext/asmjit/extconf.rb"]
31
31
 
32
- # Uncomment to register a new dependency of your gem
33
- # spec.add_dependency "example-gem", "~> 1.0"
34
-
35
- # For more information and examples about making a new gem, check out our
36
- # guide at: https://bundler.io/guides/creating_gem.html
32
+ spec.add_development_dependency "hatstone"
37
33
  end
data/ext/asmjit/asmjit.cc CHANGED
@@ -9,6 +9,9 @@ using namespace asmjit;
9
9
 
10
10
  static VALUE rb_mAsmjit;
11
11
  static VALUE rb_eAsmJITError;
12
+ static VALUE rb_cOperand;
13
+ static VALUE cX86Reg;
14
+ static VALUE cX86Mem;
12
15
 
13
16
  static JitRuntime jit_runtime;
14
17
 
@@ -54,17 +57,8 @@ VALUE code_holder_initialize(VALUE self) {
54
57
  TypedData_Get_Struct(self, CodeHolderWrapper, &code_holder_type, wrapper);
55
58
 
56
59
  CodeHolder *code = wrapper->code;
57
-
58
60
  code->init(jit_runtime.environment());
59
61
 
60
- //x86::Assembler a(code);
61
-
62
- //a.mov(x86::eax, 1);
63
- //a.ret();
64
-
65
- //int (*fn)(void);
66
- //Error err = jit_runtime.add(&fn, code);
67
-
68
62
  return self;
69
63
  }
70
64
 
@@ -80,24 +74,162 @@ VALUE code_holder_to_ptr(VALUE self) {
80
74
  return ULL2NUM(uintptr_t(fn));
81
75
  }
82
76
 
83
- struct x86AssemblerWrapper {
84
- x86::Assembler *assembler;
77
+ #define rb_define_method_id_original rb_define_method_id
78
+
79
+ VALUE code_holder_define_method(VALUE self, VALUE mod, VALUE name, VALUE arity_val) {
80
+ CodeHolderWrapper *wrapper;
81
+ TypedData_Get_Struct(self, CodeHolderWrapper, &code_holder_type, wrapper);
82
+
83
+ CodeHolder *code = wrapper->code;
84
+
85
+ int arity = FIX2INT(arity_val);
86
+ ID id = rb_sym2id(name);
87
+
88
+ VALUE (*fn)(ANYARGS);
89
+ jit_runtime.add(&fn, code);
90
+
91
+ // avoid cxxanyargs
92
+ #undef rb_define_method_id
93
+ rb_define_method_id(mod, id, fn, arity);
94
+
95
+ return name;
96
+ }
97
+
98
+ VALUE code_holder_binary(VALUE self) {
99
+ CodeHolderWrapper *wrapper;
100
+ TypedData_Get_Struct(self, CodeHolderWrapper, &code_holder_type, wrapper);
101
+
102
+ CodeHolder *code = wrapper->code;
103
+
104
+ CodeBuffer buffer = code->sectionById(0)->buffer();
105
+ return rb_str_new(
106
+ reinterpret_cast<const char *>(buffer.data()),
107
+ buffer.size()
108
+ );
109
+ }
110
+
111
+ static const rb_data_type_t operand_type = {
112
+ .wrap_struct_name = "AsmJIT::Operand",
113
+ .function = {
114
+ .dmark = NULL,
115
+ .dfree = RUBY_DEFAULT_FREE,
116
+ .dsize = NULL,
117
+ },
118
+ .data = NULL,
119
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
120
+ };
121
+
122
+ struct OperandWrapper {
123
+ Operand opnd;
124
+ };
125
+
126
+ static VALUE build_register(const char *c_name, x86::Reg reg) {
127
+ OperandWrapper *wrapper = static_cast<OperandWrapper *>(xmalloc(sizeof(OperandWrapper)));
128
+ wrapper->opnd = reg;
129
+
130
+ VALUE name = ID2SYM(rb_intern(c_name));
131
+ VALUE obj = TypedData_Wrap_Struct(cX86Reg, &operand_type, wrapper);
132
+ rb_iv_set(obj, "@name", name);
133
+ return obj;
134
+ }
135
+
136
+ static VALUE build_label(Label label) {
137
+ OperandWrapper *wrapper = static_cast<OperandWrapper *>(xmalloc(sizeof(OperandWrapper)));
138
+ wrapper->opnd = label;
139
+
140
+ VALUE obj = TypedData_Wrap_Struct(cX86Reg, &operand_type, wrapper);
141
+ return obj;
142
+ }
143
+
144
+
145
+ static Operand opnd_get(VALUE val) {
146
+ OperandWrapper *wrapper;
147
+ TypedData_Get_Struct(val, OperandWrapper, &operand_type, wrapper);
148
+ return wrapper->opnd;
149
+ }
150
+
151
+ static Label label_get(VALUE val) {
152
+ Operand opnd = opnd_get(val);
153
+ if (!opnd.isLabel()) {
154
+ rb_raise(rb_eTypeError, "operand is not label");
155
+ }
156
+ return opnd.as<Label>();
157
+ }
158
+
159
+ static x86::Reg x86_reg_get(VALUE val) {
160
+ Operand opnd = opnd_get(val);
161
+ if (!opnd.isReg()) {
162
+ rb_raise(rb_eTypeError, "operand is not reg");
163
+ }
164
+ return opnd.as<x86::Reg>();
165
+ }
166
+
167
+ static x86::Mem x86_mem_get(VALUE val) {
168
+ Operand opnd = opnd_get(val);
169
+ if (!opnd.isMem()) {
170
+ rb_raise(rb_eTypeError, "operand is not mem");
171
+ }
172
+ return opnd.as<x86::Mem>();
173
+ }
174
+
175
+ static VALUE x86_ptr(VALUE _self, VALUE regv, VALUE offsetv, VALUE sizev) {
176
+ OperandWrapper *wrapper = static_cast<OperandWrapper *>(xmalloc(sizeof(OperandWrapper)));
177
+
178
+ x86::Reg reg = x86_reg_get(regv);
179
+ if (!reg.isGp()) {
180
+ rb_raise(rb_eAsmJITError, "reg must be Gp");
181
+ }
182
+ int32_t offset = NUM2INT(offsetv);
183
+ uint32_t size = NUM2UINT(sizev);
184
+ x86::Mem mem = x86::ptr(reg.as<x86::Gp>(), offset, size);
185
+ wrapper->opnd = mem;
186
+
187
+ VALUE obj = TypedData_Wrap_Struct(cX86Mem, &operand_type, wrapper);
188
+ return obj;
189
+ }
190
+
191
+ static VALUE build_registers_hash() {
192
+ VALUE hash = rb_hash_new();
193
+
194
+ #define REGISTER(name) rb_hash_aset(hash, ID2SYM(rb_intern(#name)), build_register((#name), x86::name))
195
+
196
+ REGISTER(eax);
197
+ REGISTER(ebx);
198
+ REGISTER(ecx);
199
+ REGISTER(edx);
200
+ REGISTER(edi);
201
+ REGISTER(esi);
202
+
203
+ REGISTER(rax);
204
+ REGISTER(rbx);
205
+ REGISTER(rcx);
206
+ REGISTER(rdx);
207
+ REGISTER(rdi);
208
+ REGISTER(rsi);
209
+
210
+ #undef REGISTER
211
+
212
+ return hash;
213
+ }
214
+
215
+ struct BaseEmitterWrapper {
216
+ BaseEmitter *emitter;
85
217
  VALUE code_holder;
86
218
  };
87
219
 
88
220
  void x86_assembler_free(void *data) {
89
- x86AssemblerWrapper *wrapper = static_cast<x86AssemblerWrapper *>(data);
90
- delete wrapper->assembler;
221
+ BaseEmitterWrapper *wrapper = static_cast<BaseEmitterWrapper *>(data);
222
+ delete wrapper->emitter;
91
223
  xfree(wrapper);
92
224
  }
93
225
 
94
226
  void x86_assembler_mark(void *data) {
95
- x86AssemblerWrapper *wrapper = static_cast<x86AssemblerWrapper *>(data);
227
+ BaseEmitterWrapper *wrapper = static_cast<BaseEmitterWrapper *>(data);
96
228
  rb_gc_mark(wrapper->code_holder);
97
229
  }
98
230
 
99
- static const rb_data_type_t x86_assembler_type = {
100
- .wrap_struct_name = "AsmJIT::Assembler",
231
+ static const rb_data_type_t base_emitter_type = {
232
+ .wrap_struct_name = "AsmJIT::BaseEmitter",
101
233
  .function = {
102
234
  .dmark = NULL,
103
235
  .dfree = x86_assembler_free,
@@ -107,59 +239,73 @@ static const rb_data_type_t x86_assembler_type = {
107
239
  .flags = RUBY_TYPED_FREE_IMMEDIATELY,
108
240
  };
109
241
 
110
- VALUE x86_assembler_alloc(VALUE self) {
111
- x86AssemblerWrapper *wrapper = static_cast<x86AssemblerWrapper *>(xmalloc(sizeof(CodeHolderWrapper)));
112
- wrapper->assembler = NULL;
113
- wrapper->code_holder = Qnil;
114
-
115
- return TypedData_Wrap_Struct(self, &x86_assembler_type, wrapper);
116
- }
117
-
118
- VALUE x86_assembler_initialize(VALUE self, VALUE code_holder) {
119
- x86AssemblerWrapper *wrapper;
120
- TypedData_Get_Struct(self, x86AssemblerWrapper, &x86_assembler_type, wrapper);
242
+ VALUE x86_assembler_new(VALUE self, VALUE code_holder) {
243
+ BaseEmitterWrapper *wrapper = static_cast<BaseEmitterWrapper *>(xmalloc(sizeof(CodeHolderWrapper)));
121
244
 
122
245
  CodeHolderWrapper *code_wrapper;
123
246
  TypedData_Get_Struct(code_holder, CodeHolderWrapper, &code_holder_type, code_wrapper);
124
247
 
248
+ x86::Assembler *assembler = new x86::Assembler(code_wrapper->code);
249
+ assembler->addDiagnosticOptions(DiagnosticOptions::kValidateAssembler);
250
+
125
251
  wrapper->code_holder = code_holder;
126
- wrapper->assembler = new x86::Assembler(code_wrapper->code);
252
+ wrapper->emitter = assembler;
127
253
 
128
- return self;
254
+ return TypedData_Wrap_Struct(self, &base_emitter_type, wrapper);
129
255
  }
130
256
 
131
257
  Operand parse_operand(VALUE val) {
132
258
  if (FIXNUM_P(val)) {
133
259
  return Imm(NUM2LL(val));
134
- } else if (RB_TYPE_P(val, T_SYMBOL)) {
135
- if (val == ID2SYM(rb_intern("eax"))) {
136
- return x86::eax;
137
- } else if (val == ID2SYM(rb_intern("rax"))) {
138
- return x86::rax;
139
- }
260
+ } else if (rb_obj_is_kind_of(val, rb_cOperand)) {
261
+ return opnd_get(val);
140
262
  }
141
263
  rb_raise(rb_eAsmJITError, "bad operand: %" PRIsVALUE, val);
142
264
  }
143
265
 
144
- VALUE x86_assembler_emit(int argc, VALUE* argv, VALUE self) {
145
- x86AssemblerWrapper *wrapper;
146
- TypedData_Get_Struct(self, x86AssemblerWrapper, &x86_assembler_type, wrapper);
266
+ VALUE base_emitter_new_label(VALUE self) {
267
+ BaseEmitterWrapper *wrapper;
268
+ TypedData_Get_Struct(self, BaseEmitterWrapper, &base_emitter_type, wrapper);
269
+ BaseEmitter *emitter = wrapper->emitter;
270
+
271
+ Label label = emitter->newLabel();
272
+ return build_label(label);
273
+ }
274
+
275
+ VALUE base_emitter_bind(VALUE self, VALUE labelv) {
276
+ BaseEmitterWrapper *wrapper;
277
+ TypedData_Get_Struct(self, BaseEmitterWrapper, &base_emitter_type, wrapper);
278
+ BaseEmitter *emitter = wrapper->emitter;
147
279
 
148
- x86::Assembler *assembler = wrapper->assembler;
280
+ Label label = label_get(labelv);
281
+
282
+ int err = emitter->bind(label);
283
+ if (err) {
284
+ rb_raise(rb_eAsmJITError, "error binding label");
285
+ }
286
+
287
+ return labelv;
288
+ }
289
+
290
+ VALUE base_emitter_emit(int argc, VALUE* argv, VALUE self) {
291
+ BaseEmitterWrapper *wrapper;
292
+ TypedData_Get_Struct(self, BaseEmitterWrapper, &base_emitter_type, wrapper);
293
+
294
+ BaseEmitter *emitter = wrapper->emitter;
149
295
 
150
296
  if (argc < 1) return Qnil;
151
297
  if (argc > 7) return Qnil;
152
298
 
153
299
  VALUE insn_name = argv[0];
154
- InstId inst_id = InstAPI::stringToInstId(assembler->arch(), RSTRING_PTR(insn_name), RSTRING_LEN(insn_name));
300
+ Check_Type(insn_name, T_STRING);
301
+ InstId inst_id = InstAPI::stringToInstId(emitter->arch(), RSTRING_PTR(insn_name), RSTRING_LEN(insn_name));
155
302
 
156
303
  Operand operands[6];
157
304
  for (int i = 0; i < argc - 1; i++) {
158
305
  operands[i] = parse_operand(argv[i + 1]);
159
306
  }
160
307
 
161
- //assembler->emit(inst_id);
162
- assembler->emitOpArray(inst_id, &operands[0], argc - 1);
308
+ emitter->emitOpArray(inst_id, &operands[0], argc - 1);
163
309
 
164
310
  return self;
165
311
  }
@@ -177,17 +323,32 @@ Init_asmjit(void)
177
323
 
178
324
  rb_eAsmJITError = rb_define_class_under(rb_mAsmjit, "Error", rb_eStandardError);
179
325
 
180
- VALUE cCodeHolder = rb_define_class_under(rb_mAsmjit, "CodeHolder", rb_cObject);
181
- rb_define_alloc_func(cCodeHolder, code_holder_alloc);
182
- rb_define_method(cCodeHolder, "initialize", code_holder_initialize, 0);
183
- rb_define_method(cCodeHolder, "to_ptr", code_holder_to_ptr, 0);
326
+ VALUE cCodeHolder = rb_define_class_under(rb_mAsmjit, "CodeHolder", rb_cObject);
327
+ rb_define_alloc_func(cCodeHolder, code_holder_alloc);
328
+ rb_define_method(cCodeHolder, "initialize", code_holder_initialize, 0);
329
+ rb_define_method(cCodeHolder, "to_ptr", code_holder_to_ptr, 0);
330
+ rb_define_method(cCodeHolder, "def_method", code_holder_define_method, 3);
331
+ rb_define_method(cCodeHolder, "binary", code_holder_binary, 0);
332
+
333
+ VALUE rb_mX86 = rb_define_module_under(rb_mAsmjit, "X86");
334
+
335
+ VALUE rb_cBaseEmitter = rb_define_class_under(rb_mAsmjit, "BaseEmitter", rb_cObject);
336
+ rb_undef_alloc_func(rb_cBaseEmitter);
337
+ rb_define_method(rb_cBaseEmitter, "_emit", base_emitter_emit, -1);
338
+ rb_define_method(rb_cBaseEmitter, "new_label", base_emitter_new_label, 0);
339
+ rb_define_method(rb_cBaseEmitter, "bind", base_emitter_bind, 1);
340
+
341
+ VALUE cX86Assembler = rb_define_class_under(rb_mX86, "Assembler", rb_cBaseEmitter);
342
+ rb_define_singleton_method(cX86Assembler, "new", x86_assembler_new, 1);
343
+
344
+ rb_cOperand = rb_define_class_under(rb_mAsmjit, "Operand", rb_cObject);
345
+ rb_undef_alloc_func(rb_cOperand);
184
346
 
185
- VALUE rb_mX86 = rb_define_module_under(rb_mAsmjit, "X86");
347
+ cX86Reg = rb_define_class_under(rb_mX86, "Reg", rb_cOperand);
348
+ rb_define_attr(cX86Reg, "name", 1, 0);
186
349
 
187
- VALUE cX86Assembler = rb_define_class_under(rb_mX86, "Assembler", rb_cObject);
188
- rb_define_alloc_func(cX86Assembler, x86_assembler_alloc);
189
- rb_define_method(cX86Assembler, "initialize", x86_assembler_initialize, 1);
190
- rb_define_method(cX86Assembler, "emit", x86_assembler_emit, -1);
350
+ cX86Mem = rb_define_class_under(rb_mX86, "Mem", rb_cOperand);
351
+ rb_define_singleton_method(cX86Mem, "new", x86_ptr, 3);
191
352
 
192
353
  VALUE instructions = rb_ary_new();
193
354
 
@@ -203,4 +364,5 @@ Init_asmjit(void)
203
364
  }
204
365
 
205
366
  rb_define_const(rb_mX86, "INSTRUCTIONS", instructions);
367
+ rb_define_const(rb_mX86, "REGISTERS", build_registers_hash());
206
368
  }
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AsmJIT
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/asmjit.rb CHANGED
@@ -3,14 +3,80 @@
3
3
  require_relative "asmjit/version"
4
4
  require_relative "asmjit/asmjit"
5
5
 
6
+ AsmJit = AsmJIT
6
7
  module AsmJIT
8
+ def self.assemble
9
+ code = CodeHolder.new
10
+ yield code.assembler
11
+ code
12
+ end
13
+
14
+ class CodeHolder
15
+ def assembler
16
+ X86::Assembler.new(self)
17
+ end
18
+
19
+ def to_fiddle(inputs: nil, output: nil)
20
+ ptr = to_ptr
21
+
22
+ Fiddle::Function.new(
23
+ ptr,
24
+ inputs || [],
25
+ output || Fiddle::TYPE_INT
26
+ )
27
+ end
28
+
29
+ def def_module(arity, method_name: :call)
30
+ mod = Module.new
31
+ self.def_method(mod, method_name, arity)
32
+ mod
33
+ end
34
+
35
+ def def_class(arity, method_name: :call)
36
+ mod = Class.new
37
+ self.def_method(mod, method_name, arity)
38
+ mod
39
+ end
40
+ end
41
+
7
42
  module X86
43
+ module Helpers
44
+ extend self
45
+
46
+ def parse_operand(arg)
47
+ if Symbol === arg && reg = REGISTERS[arg]
48
+ reg
49
+ else
50
+ arg
51
+ end
52
+ end
53
+ end
54
+
55
+ class << self
56
+ def ptr(base, offset, size)
57
+ X86::Mem.new(Helpers.parse_operand(base), offset, size)
58
+ end
59
+
60
+ def qword_ptr(base, offset)
61
+ ptr(base, offset, 8)
62
+ end
63
+ end
64
+
8
65
  class Assembler
66
+ def emit(*args)
67
+ _emit(*(args.map { |arg| Helpers.parse_operand(arg) }))
68
+ end
69
+
9
70
  INSTRUCTIONS.each do |name|
10
71
  define_method(name) do |*args|
11
72
  emit(name, *args)
12
73
  end
13
74
  end
14
75
  end
76
+ class Reg
77
+ def inspect
78
+ "#<#{self.class} #{name}>"
79
+ end
80
+ end
15
81
  end
16
82
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asmjit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Hawthorn
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-07 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2022-09-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hatstone
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: 'Ruby wrapper for asmjit: a lightweight library for machine code generation'
14
28
  email:
15
29
  - john@hawthorn.email
@@ -53,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  requirements: []
56
- rubygems_version: 3.3.3
70
+ rubygems_version: 3.3.7
57
71
  signing_key:
58
72
  specification_version: 4
59
73
  summary: 'Ruby wrapper for asmjit: a lightweight library for machine code generation'