aarch64 2.0.2 → 2.1.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4148547f29912a9cea29d06ba88ebafff8ffd701b187ab9aa3a0b4ee1d309b0
4
- data.tar.gz: dc7b08ee7be2e57acfb2d5181d374a9baf6806be65dee0318cd95417349a2cfc
3
+ metadata.gz: ea2cf209e63aaf787b7a0a05f5d1788c3bfa36f1cee6fa1a812a99e2de5e42eb
4
+ data.tar.gz: 8d60abcae40ed475e8f3756892f432a88e99501e23f5df2d113e02b64042290f
5
5
  SHA512:
6
- metadata.gz: f215a62f40c5ad36e81ec7d1d1110e21639ab0b19950283f4b5dbde46e5b26c17081a527d41dca600c4a8e8aa07eb0fc5f1f21a68d4425084c5eaf94de8baaef
7
- data.tar.gz: 4d0027eeb4a45b5d77e409b2fc343d59ec19548b4874f1c2e61bfeabcecb991ee2527a0969133f1029aa13e2623361bef3e6637ed65f9617001f5709ab31b99a
6
+ metadata.gz: 44a6660bab9ee5db6f80b4714e8fc53d64f351f1ba2c9b2293826d83215423944fd8a2b444ea4cded815b09ed70f2bef289eb25ef5ff596dc781ceeb781a4643
7
+ data.tar.gz: b6e384d4d8743cd74a4324fb7e2e50f1f3317446a8d0ff2518ccecabed09634f8c0126e8831af21e5d57284254e3c9d5f83e578f924bc2e2d84b0f05b6e3ac4a
data/README.md CHANGED
@@ -70,6 +70,32 @@ asm.movk X0, 0xF00D, lsl: 16
70
70
  asm.ret
71
71
  ```
72
72
 
73
+ Here is another example of the same assembly, but using the built-in ARM64
74
+ assembly parser:
75
+
76
+ ```ruby
77
+ require "jit_buffer"
78
+ require "aarch64/parser"
79
+
80
+ parser = AArch64::Parser.new
81
+ asm = parser.parse <<~eoasm
82
+ movz x0, 0xCAFE
83
+ movk x0, 0xF00D, lsl #16
84
+ ret
85
+ eoasm
86
+
87
+ # create a JIT buffer
88
+ jit_buffer = JITBuffer.new 4096
89
+
90
+ # Write the instructions to a JIT buffer
91
+ jit_buffer.writeable!
92
+ asm.write_to jit_buffer
93
+ jit_buffer.executable!
94
+
95
+ # Execute the JIT buffer
96
+ p jit_buffer.to_function([], -Fiddle::TYPE_INT).call.to_s(16) # => f00dcafe
97
+ ```
98
+
73
99
  ## Hacking / Contributing
74
100
 
75
101
  Hacking on this gem should be similar to most. Just do:
data/Rakefile CHANGED
@@ -162,7 +162,7 @@ end
162
162
 
163
163
  rule ".tab.rb" => [".y"] do |t|
164
164
  puts "#" * 90
165
- sh "gel exec racc -E -v #{t.source}"
165
+ sh "gel exec racc -v #{t.source}"
166
166
  puts "#" * 90
167
167
  end
168
168
 
@@ -205,3 +205,43 @@ task "autotest" do
205
205
  end
206
206
  end
207
207
  end
208
+
209
+ # Make a test from the assembly in stdin.
210
+ # $ echo "brk #0x1" | rake make_test
211
+ # assert_bytes [0x20, 00, 0x20, 0xd4] do |asm|
212
+ # asm.brk #0x1
213
+ # end
214
+ task :make_test do
215
+ require "odinflex/mach-o"
216
+ require "tempfile"
217
+
218
+ begin
219
+ asm = $stdin.read
220
+ bin = Tempfile.new("bin")
221
+ source = Tempfile.new("asm")
222
+ source.puts ".global _start"
223
+ source.puts ".align 2"
224
+ source.puts "_start:"
225
+ source.puts
226
+ source.write asm
227
+ source.flush
228
+ system("as -o #{bin.path} #{source.path}")
229
+
230
+ my_macho = OdinFlex::MachO.new bin
231
+ my_macho.each do |section|
232
+ if section.section? && section.sectname == "__text"
233
+ bin.seek(section.offset, IO::SEEK_SET)
234
+ (section.size / 4).times do
235
+ bytes = bin.read(4).unpack("C4").map { |x| sprintf("%#02x", x) }.join(", ")
236
+ puts "assert_bytes [#{bytes}] do |asm|"
237
+ puts " asm.#{asm}"
238
+ puts "end"
239
+ end
240
+ #p section.start_pos
241
+ end
242
+ end
243
+ ensure
244
+ bin.unlink
245
+ source.unlink
246
+ end
247
+ end
data/aarch64.gemspec CHANGED
@@ -14,9 +14,10 @@ Gem::Specification.new do |s|
14
14
  s.homepage = "https://github.com/tenderlove/aarch64"
15
15
  s.license = "Apache-2.0"
16
16
 
17
+ s.add_runtime_dependency 'racc', '~> 1.6'
17
18
  s.add_development_dependency 'hatstone', '~> 1.0.0'
18
19
  s.add_development_dependency 'jit_buffer', '~> 1.0.0'
19
20
  s.add_development_dependency 'minitest', '~> 5.15'
20
21
  s.add_development_dependency 'rake', '~> 13.0'
21
- s.add_development_dependency 'racc', '~> 1.6'
22
+ s.add_development_dependency 'odinflex', '~> 1.0'
22
23
  end
@@ -10,7 +10,7 @@ module AArch64
10
10
  end
11
11
 
12
12
  def encode _
13
- label = @label / 4096
13
+ label = unwrap_label(@label, 0) / 4096
14
14
  ADRP(label & 0x3, check_mask(label >> 2, 0x7ffff), @xd)
15
15
  end
16
16
 
@@ -152,9 +152,20 @@ module AArch64
152
152
 
153
153
  def parse str
154
154
  str += "\n" unless str.end_with?("\n")
155
- @scan = StringScanner.new str
156
- @asm = AArch64::Assembler.new
157
- do_parse
155
+ parse_states = [
156
+ # First pass: Label parsing
157
+ :first_pass,
158
+ # Second pass: Code generation
159
+ :second_pass
160
+ ]
161
+ @labels = {}
162
+ parse_states.each do |state|
163
+ @scan = StringScanner.new str
164
+ @asm = AArch64::Assembler.new
165
+ @state = state
166
+ do_parse
167
+ end
168
+
158
169
  @asm
159
170
  end
160
171
 
@@ -166,12 +177,394 @@ module AArch64
166
177
  SYS_REG_MAP = Hash[AArch64::SystemRegisters.constants.map { |k|
167
178
  [k.to_s.downcase, AArch64::SystemRegisters.const_get(k)]
168
179
  }]
180
+ # Created with: puts File.read('./lib/aarch64/parser.y').scan(/\b[A-Z][A-Z\d]+\b/).sort.uniq - ['LABEL', 'LABEL_CREATE']
181
+ KEYWORDS = %w[
182
+ ADC
183
+ ADCS
184
+ ADD
185
+ ADDS
186
+ ADR
187
+ ADRP
188
+ ALLE1
189
+ ALLE1IS
190
+ ALLE1OS
191
+ ALLE2
192
+ ALLE2IS
193
+ ALLE2OS
194
+ ALLE3
195
+ ALLE3IS
196
+ ALLE3OS
197
+ AND
198
+ ANDS
199
+ ASIDE1
200
+ ASIDE1IS
201
+ ASIDE1OS
202
+ ASR
203
+ AT
204
+ AUTDA
205
+ B
206
+ BANG
207
+ BFI
208
+ BFXIL
209
+ BIC
210
+ BICS
211
+ BL
212
+ BLR
213
+ BR
214
+ BRK
215
+ CBNZ
216
+ CBZ
217
+ CCMN
218
+ CCMP
219
+ CGDSW
220
+ CGDVAC
221
+ CGDVADP
222
+ CGDVAP
223
+ CGSW
224
+ CGVAC
225
+ CGVADP
226
+ CGVAP
227
+ CIGDSW
228
+ CIGDVAC
229
+ CIGSW
230
+ CIGVAC
231
+ CINC
232
+ CINV
233
+ CISW
234
+ CIVAC
235
+ CLREX
236
+ CLS
237
+ CLZ
238
+ CMN
239
+ CMP
240
+ CNEG
241
+ COMMA
242
+ CRC32B
243
+ CRC32CB
244
+ CRC32CH
245
+ CRC32CW
246
+ CRC32CX
247
+ CRC32H
248
+ CRC32W
249
+ CRC32X
250
+ CSEL
251
+ CSET
252
+ CSETM
253
+ CSINC
254
+ CSINV
255
+ CSNEG
256
+ CSW
257
+ CVAC
258
+ CVADP
259
+ CVAP
260
+ CVAU
261
+ DC
262
+ DCPS1
263
+ DCPS2
264
+ DCPS3
265
+ DMB
266
+ DOT
267
+ DRPS
268
+ DSB
269
+ EOL
270
+ EON
271
+ EOR
272
+ EQ
273
+ ERET
274
+ EXTR
275
+ GE
276
+ GT
277
+ GVA
278
+ GZVA
279
+ HI
280
+ HINT
281
+ HLT
282
+ HS
283
+ HVC
284
+ IALLU
285
+ IALLUIS
286
+ IC
287
+ IGDSW
288
+ IGDVAC
289
+ IGSW
290
+ IGVAC
291
+ IPAS2E1
292
+ IPAS2E1IS
293
+ IPAS2E1OS
294
+ IPAS2LE1
295
+ IPAS2LE1IS
296
+ IPAS2LE1OS
297
+ ISB
298
+ ISH
299
+ ISHLD
300
+ ISHST
301
+ ISW
302
+ IVAC
303
+ IVAU
304
+ LD
305
+ LDAR
306
+ LDARB
307
+ LDARH
308
+ LDAXP
309
+ LDAXR
310
+ LDAXRB
311
+ LDAXRH
312
+ LDNP
313
+ LDP
314
+ LDPSW
315
+ LDR
316
+ LDRB
317
+ LDRH
318
+ LDRSB
319
+ LDRSH
320
+ LDRSW
321
+ LDTR
322
+ LDTRB
323
+ LDTRH
324
+ LDTRSB
325
+ LDTRSH
326
+ LDTRSW
327
+ LDUR
328
+ LDURB
329
+ LDURH
330
+ LDURSB
331
+ LDURSH
332
+ LDURSW
333
+ LDXP
334
+ LDXR
335
+ LDXRB
336
+ LDXRH
337
+ LE
338
+ LO
339
+ LS
340
+ LSL
341
+ LSQ
342
+ LSR
343
+ LT
344
+ MADD
345
+ MI
346
+ MNEG
347
+ MOV
348
+ MOVK
349
+ MOVN
350
+ MOVZ
351
+ MRS
352
+ MSR
353
+ MSUB
354
+ MUL
355
+ MVN
356
+ NE
357
+ NEG
358
+ NEGS
359
+ NGC
360
+ NGCS
361
+ NOP
362
+ NSH
363
+ NSHLD
364
+ NSHST
365
+ NUMBER
366
+ ORN
367
+ ORR
368
+ OSH
369
+ OSHLD
370
+ OSHST
371
+ PL
372
+ PRFM
373
+ PRFOP
374
+ PRFUM
375
+ PSSBB
376
+ RBIT
377
+ RET
378
+ REV
379
+ REV16
380
+ REV32
381
+ RIPAS2E1
382
+ RIPAS2E1IS
383
+ RIPAS2E1OS
384
+ RIPAS2LE1
385
+ RIPAS2LE1IS
386
+ RIPAS2LE1OS
387
+ ROR
388
+ RSQ
389
+ RVAAE1
390
+ RVAAE1IS
391
+ RVAAE1OS
392
+ RVAALE1
393
+ RVAALE1IS
394
+ RVAALE1OS
395
+ RVAE1
396
+ RVAE1IS
397
+ RVAE1OS
398
+ RVAE2
399
+ RVAE2IS
400
+ RVAE2OS
401
+ RVAE3
402
+ RVAE3IS
403
+ RVAE3OS
404
+ RVALE1
405
+ RVALE1IS
406
+ RVALE1OS
407
+ RVALE2
408
+ RVALE2IS
409
+ RVALE2OS
410
+ RVALE3
411
+ RVALE3IS
412
+ RVALE3OS
413
+ S12E0R
414
+ S12E0W
415
+ S12E1R
416
+ S12E1W
417
+ S1E0R
418
+ S1E0W
419
+ S1E1R
420
+ S1E1RP
421
+ S1E1W
422
+ S1E1WP
423
+ S1E2R
424
+ S1E2W
425
+ S1E3R
426
+ S1E3W
427
+ SBC
428
+ SBCS
429
+ SBFIZ
430
+ SBFX
431
+ SDIV
432
+ SEV
433
+ SEVL
434
+ SMADDL
435
+ SMC
436
+ SMNEGL
437
+ SMSUBL
438
+ SMULH
439
+ SMULL
440
+ SP
441
+ SSBB
442
+ ST
443
+ STLR
444
+ STLRB
445
+ STLRH
446
+ STLXP
447
+ STLXR
448
+ STLXRB
449
+ STLXRH
450
+ STNP
451
+ STP
452
+ STR
453
+ STRB
454
+ STRH
455
+ STTR
456
+ STTRB
457
+ STTRH
458
+ STUR
459
+ STURB
460
+ STURH
461
+ STXP
462
+ STXR
463
+ STXRB
464
+ STXRH
465
+ SUB
466
+ SUBS
467
+ SVC
468
+ SXTB
469
+ SXTH
470
+ SXTW
471
+ SXTX
472
+ SY
473
+ SYS
474
+ SYSL
475
+ SYSTEMREG
476
+ TBNZ
477
+ TBZ
478
+ TLBI
479
+ TST
480
+ UBFIZ
481
+ UBFX
482
+ UDIV
483
+ UMADDL
484
+ UMNEGL
485
+ UMSUBL
486
+ UMULH
487
+ UMULL
488
+ UXTB
489
+ UXTH
490
+ UXTW
491
+ UXTX
492
+ VAAE1
493
+ VAAE1IS
494
+ VAAE1OS
495
+ VAALE1
496
+ VAALE1IS
497
+ VAALE1OS
498
+ VAE1
499
+ VAE1IS
500
+ VAE1OS
501
+ VAE2
502
+ VAE2IS
503
+ VAE2OS
504
+ VAE3
505
+ VAE3IS
506
+ VAE3OS
507
+ VALE1
508
+ VALE1IS
509
+ VALE1OS
510
+ VALE2
511
+ VALE2IS
512
+ VALE2OS
513
+ VALE3
514
+ VALE3IS
515
+ VALE3OS
516
+ VC
517
+ VMALLE1
518
+ VMALLE1IS
519
+ VMALLE1OS
520
+ VMALLS12E1
521
+ VMALLS12E1IS
522
+ VMALLS12E1OS
523
+ VS
524
+ WFE
525
+ WFI
526
+ WSP
527
+ XZR
528
+ YIELD
529
+ ZVA
530
+ ].freeze
531
+ KEYWORDS_SCAN = /(#{Regexp.union(KEYWORDS.sort).source})\b/i
532
+ LABEL_SCAN = /[a-zA-Z_]\w+/
533
+ LABEL_CREATE_SCAN = /#{LABEL_SCAN}:/
534
+
535
+ def register_label str
536
+ return unless @state == :first_pass
537
+
538
+ if @labels.include?(str)
539
+ raise "symbol '#{str}' is already defined"
540
+ end
541
+
542
+ label = @asm.make_label(str)
543
+ @asm.put_label(label)
544
+ @labels[str] = label
545
+
546
+ nil
547
+ end
548
+
549
+ def label_for str
550
+ # In the first pass, all valid labels are not known yet i.e. forward references
551
+ # Return a placeholder value instead.
552
+ if @state == :first_pass
553
+ @asm.make_label(str)
554
+ else
555
+ raise("Label #{str.inspect} not defined") unless @labels.key?(str)
556
+ @labels[str]
557
+ end
558
+ end
169
559
 
170
560
  def _next_token
171
561
  return _next_token if @scan.scan(/[\t ]+/) # skip whitespace
562
+ return if @scan.eos?
172
563
 
173
564
  if str = @scan.scan(/\n/)
174
565
  return [:EOL, :EOL]
566
+ elsif str = @scan.scan(LABEL_CREATE_SCAN)
567
+ [:LABEL_CREATE, str[0...-1]]
175
568
  elsif str = @scan.scan(/x\d+/i)
176
569
  [:Xd, AArch64::Registers.const_get(str.upcase)]
177
570
  elsif str = @scan.scan(/w\d+/i)
@@ -218,10 +611,20 @@ module AArch64
218
611
  end
219
612
  elsif str = @scan.scan(SYS_REG_SCAN)
220
613
  [:SYSTEMREG, SYS_REG_MAP[str.downcase]]
221
- elsif str = @scan.scan(/\w+/)
614
+ elsif str = @scan.scan(KEYWORDS_SCAN)
222
615
  [str.upcase.to_sym, str]
616
+ elsif str = @scan.scan(LABEL_SCAN)
617
+ [:LABEL, str]
223
618
  else
619
+ [:UNKNOWN_CHAR, @scan.getch]
224
620
  end
225
621
  end
622
+
623
+ def on_error(token_id, val, vstack)
624
+ token_str = token_to_str(token_id) || '?'
625
+ string = @scan.string
626
+ line_number = string.byteslice(0, @scan.pos).count("\n") + 1
627
+ raise ParseError, "parse error on value #{val.inspect} (#{token_str}) on line #{line_number} at pos #{@scan.pos}/#{string.bytesize}"
628
+ end
226
629
  end
227
630
  end