NCPrePatcher 0.2.0-x86_64-darwin-24

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.
@@ -0,0 +1,836 @@
1
+ require 'ffi'
2
+
3
+ module UnarmBind
4
+ extend FFI::Library
5
+ ffi_lib [
6
+ File.expand_path("unarm", __dir__),
7
+ File.expand_path("unarm.dylib", __dir__),
8
+ File.expand_path("unarm.so", __dir__),
9
+ ]
10
+
11
+ typedef :pointer, :ins_handle
12
+ typedef :pointer, :cstr_handle
13
+ typedef :pointer, :parser_handle
14
+ typedef :pointer, :symbols_handle
15
+ typedef :pointer, :ins_args_handle
16
+
17
+ attach_function :arm9_new_arm_ins, [:uint32], :ins_handle
18
+ attach_function :arm9_new_thumb_ins, [:uint32], :ins_handle
19
+ attach_function :arm7_new_arm_ins, [:uint32], :ins_handle
20
+ attach_function :arm7_new_thumb_ins, [:uint32], :ins_handle
21
+
22
+ attach_function :arm9_new_parser, [:uint32], :parser_handle
23
+ attach_function :arm7_new_parser, [:uint32], :parser_handle
24
+
25
+ attach_function :arm9_arm_ins_to_str, [:ins_handle], :cstr_handle
26
+ attach_function :arm7_arm_ins_to_str, [:ins_handle], :cstr_handle
27
+ attach_function :arm9_thumb_ins_to_str, [:ins_handle], :cstr_handle
28
+ attach_function :arm7_thumb_ins_to_str, [:ins_handle], :cstr_handle
29
+
30
+ attach_function :arm9_arm_ins_to_str_with_syms,
31
+ [:ins_handle, :symbols_handle, :uint32, :uint32, :int32], :cstr_handle
32
+ attach_function :arm7_arm_ins_to_str_with_syms,
33
+ [:ins_handle, :symbols_handle, :uint32, :uint32, :int32], :cstr_handle
34
+ attach_function :arm9_thumb_ins_to_str_with_syms,
35
+ [:ins_handle, :symbols_handle, :uint32, :uint32, :int32], :cstr_handle
36
+ attach_function :arm7_thumb_ins_to_str_with_syms,
37
+ [:ins_handle, :symbols_handle, :uint32, :uint32, :int32], :cstr_handle
38
+
39
+ attach_function :arm9_arm_ins_get_args, [:ins_handle], :ins_args_handle
40
+ attach_function :arm7_arm_ins_get_args, [:ins_handle], :ins_args_handle
41
+ attach_function :arm9_thumb_ins_get_args, [:ins_handle], :ins_args_handle
42
+ attach_function :arm7_thumb_ins_get_args, [:ins_handle], :ins_args_handle
43
+
44
+ attach_function :arm9_arm_ins_get_opcode_id, [:ins_handle], :uint16
45
+ attach_function :arm7_arm_ins_get_opcode_id, [:ins_handle], :uint16
46
+ attach_function :arm9_thumb_ins_get_opcode_id, [:ins_handle], :uint16
47
+ attach_function :arm7_thumb_ins_get_opcode_id, [:ins_handle], :uint16
48
+
49
+ attach_function :arm_ins_is_conditional, [:ins_handle], :bool
50
+ attach_function :thumb_ins_is_conditional, [:ins_handle], :bool
51
+
52
+ attach_function :arm_ins_updates_condition_flags, [:ins_handle], :bool
53
+ attach_function :thumb_ins_updates_condition_flags, [:ins_handle], :bool
54
+
55
+ attach_function :arm_ins_is_data_operation, [:ins_handle], :bool
56
+ attach_function :thumb_ins_is_data_operation, [:ins_handle], :bool
57
+
58
+ attach_function :get_sym_for_addr, [:uint32, :symbols_handle, :uint32], :cstr_handle
59
+
60
+ attach_function :free_arm_ins, [:ins_handle], :void
61
+ attach_function :free_thumb_ins, [:ins_handle], :void
62
+ attach_function :free_ins_args, [:ins_args_handle, :uint32], :void
63
+ attach_function :arm9_free_parser, [:parser_handle], :void
64
+ attach_function :arm7_free_parser, [:parser_handle], :void
65
+ attach_function :free_c_str, [:cstr_handle], :void
66
+
67
+ # NOTE: some of the following instructions are not valid in ARMv5TE/v4T
68
+ OPCODE = [
69
+ :illegal, :adc, :add, :and, :asr, :b, :bl, :bic, :bkpt, :blxi,
70
+ :blxr, :bx, :bxj, :cdp, :cdp2, :clrex, :clz, :cmn, :cmp, :cps,
71
+ :csdb, :dbg, :eor, :ldc, :ldc2, :ldmw, :ldm, :ldmp, :ldmpw, :ldmpcw,
72
+ :ldmpc, :ldr, :ldrb, :ldrbt, :ldrd, :ldrex, :ldrexb, :ldrexd, :ldrexh, :ldrh,
73
+ :ldrsb, :ldrsh, :ldrt, :lsl, :lsr, :mcr, :mcr2, :mcrr, :mcrr2, :mla,
74
+ :mov, :movimm, :movreg, :mrc, :mrc2, :mrrc, :mrrc2, :mrs, :msri, :msr,
75
+ :mul, :mvn, :nop, :orr, :pkhbt, :pkhtb, :pld, :popm, :popr, :pushm,
76
+ :pushr, :qadd, :qadd16, :qadd8, :qasx, :qdadd, :qdsub, :qsax, :qsub, :qsub16,
77
+ :qsub8, :rev, :rev16, :revsh, :rfe, :ror, :rrx, :rsb, :rsc, :sadd16,
78
+ :sadd8, :sasx, :sbc, :sel, :setend, :sev, :shadd16, :shadd8, :shasx, :shsax,
79
+ :shsub16, :shsub8, :smla, :smlad, :smlal, :smlalxy, :smlald, :smlaw, :smlsd, :smlsld,
80
+ :smmla, :smmls, :smmul, :smuad, :smul, :smull, :smulw, :smusd, :srs, :ssat,
81
+ :ssat16, :ssax, :ssub16, :ssub8, :stc, :stc2, :stm, :stmw, :stmp, :stmpw,
82
+ :str, :strb, :strbt, :strd, :strex, :strexb, :strexd, :strexh, :strh, :strt,
83
+ :sub, :svc, :swi, :swp, :swpb, :sxtab, :sxtab16, :sxtah, :sxtb, :sxtb16,
84
+ :sxth, :teq, :tst, :uadd16, :uadd8, :uasx, :udf, :uhadd16, :uhadd8, :uhasx,
85
+ :uhsax, :uhsub16, :uhsub8, :umaal, :umlal, :umull, :uqadd16, :uqadd8, :uqasx, :uqsax,
86
+ :uqsub16, :uqsub8, :usad8, :usada8, :usat, :usat16, :usax, :usub16, :usub8, :uxtab,
87
+ :uxtab16, :uxtah, :uxtb, :uxtb16, :uxth, :wfe, :wfi, :yield
88
+ ].freeze
89
+
90
+ OPCODE_MNEMONIC = [
91
+ '<illegal>', 'adc', 'add', 'and', 'asr', 'b', 'bl', 'bic', 'bkpt', 'blx',
92
+ 'blx', 'bx', 'bxj', 'cdp', 'cdp2', 'clrex', 'clz', 'cmn', 'cmp', 'cps',
93
+ 'csdb', 'dbg', 'eor', 'ldc', 'ldc2', 'ldm', 'ldm', 'ldm', 'ldm', 'ldm',
94
+ 'ldm', 'ldr', 'ldrb', 'ldrbt', 'ldrd', 'ldrex', 'ldrexb', 'ldrexd', 'ldrexh', 'ldrh',
95
+ 'ldrsb', 'ldrsh', 'ldrt', 'lsl', 'lsr', 'mcr', 'mcr2', 'mcrr', 'mcrr2', 'mla',
96
+ 'mov', 'mov', 'mov', 'mrc', 'mrc2', 'mrrc', 'mrrc2', 'mrs', 'msr', 'msr',
97
+ 'mul', 'mvn', 'nop', 'orr', 'pkhbt', 'pkhtb', 'pld', 'pop', 'pop', 'push',
98
+ 'push', 'qadd', 'qadd16', 'qadd8', 'qasx', 'qdadd', 'qdsub', 'qsax', 'qsub', 'qsub16',
99
+ 'qsub8', 'rev', 'rev16', 'revsh', 'rfe', 'ror', 'rrx', 'rsb', 'rsc', 'sadd16',
100
+ 'sadd8', 'sasx', 'sbc', 'sel', 'setend', 'sev', 'shadd16', 'shadd8', 'shasx', 'shsax',
101
+ 'shsub16', 'shsub8', 'smla', 'smlad', 'smlal', 'smlal', 'smlald', 'smlaw', 'smlsd', 'smlsld',
102
+ 'smmla', 'smmls', 'smmul', 'smuad', 'smul', 'smull', 'smulw', 'smusd', 'srs', 'ssat',
103
+ 'ssat16', 'ssax', 'ssub16', 'ssub8', 'stc', 'stc2', 'stm', 'stm', 'stm', 'stm',
104
+ 'str', 'strb', 'strbt', 'strd', 'strex', 'strexb', 'strexd', 'strexh', 'strh', 'strt',
105
+ 'sub', 'svc', 'swi', 'swp', 'swpb', 'sxtab', 'sxtab16', 'sxtah', 'sxtb', 'sxtb16',
106
+ 'sxth', 'teq', 'tst', 'uadd16', 'uadd8', 'uasx', 'udf', 'uhadd16', 'uhadd8', 'uhasx',
107
+ 'uhsax', 'uhsub16', 'uhsub8', 'umaal', 'umlal', 'umull', 'uqadd16', 'uqadd8', 'uqasx', 'uqsax',
108
+ 'uqsub16', 'uqsub8', 'usad8', 'usada8', 'usat', 'usat16', 'usax', 'usub16', 'usub8', 'uxtab',
109
+ 'uxtab16', 'uxtah', 'uxtb', 'uxtb16', 'uxth', 'wfe', 'wfi', 'yield'
110
+ ].freeze
111
+
112
+ CONDITION = [:illegal, :eq, :ne, :hs, :lo, :mi, :pl, :vs, :vc, :hi, :ls, :ge, :lt, :gt, :le, :al].freeze
113
+
114
+ REGISTER = [:r0, :r1, :r2, :r3, :r4, :r5, :r6, :r7, :r8, :r9, :r10, :r11, :r12, :sp, :lr, :pc].freeze
115
+
116
+ SHIFT = [:lsl, :lsr, :asr, :ror, :rrx].freeze
117
+
118
+ CO_REG = [:c0, :c1, :c2, :c3, :c4, :c5, :c6, :c7, :c8, :c9, :c10, :c11, :c12, :c13, :c14, :c15].freeze
119
+
120
+ STATUS_REG = [:cpsr, :spsr].freeze
121
+
122
+ ARGUMENT_KIND = [
123
+ :none, :reg, :reg_list, :co_reg, :status_reg, :status_mask, :shift, :shift_imm, :shift_reg,
124
+ :u_imm, :sat_imm, :s_imm, :offset_imm, :offset_reg, :branch_dest, :co_option, :co_opcode,
125
+ :coproc_num, :cpsr_mode, :cpsr_flags, :endian
126
+ ].freeze
127
+
128
+ ENDIAN = [:le, :be].freeze # illegal=255
129
+
130
+ CONDITION_MAP = CONDITION.each_with_index.to_h
131
+ REGISTER_MAP = REGISTER.each_with_index.to_h
132
+ SHIFT_MAP = SHIFT.each_with_index.to_h
133
+ CO_REG_MAP = CO_REG.each_with_index.to_h
134
+ STATUS_REG_MAP = STATUS_REG.each_with_index.to_h
135
+ ARGUMENT_KIND_MAP = ARGUMENT_KIND.each_with_index.to_h
136
+ ENDIAN_MAP = ENDIAN.each_with_index.to_h
137
+
138
+ class RegList < FFI::Struct
139
+ layout :regs, :uint32, # bitfield of registers
140
+ :user_mode, :bool # access user-mode registers from elevated mode
141
+
142
+ def contains?(register)
143
+ register = REGISTER_MAP[register] if register.is_a? Symbol
144
+ raise 'Invalid register' if register.nil? || (register.is_a?(Integer) && register >= REGISTER_MAP.length)
145
+ self[:regs] & (1 << register) != 0
146
+ end
147
+
148
+ def user_mode?
149
+ self[:user_mode]
150
+ end
151
+ end
152
+
153
+ class Reg < FFI::Struct
154
+ layout :deref, :bool, # use as a base register
155
+ :reg, :uint8, # Register
156
+ :writeback, :bool # when used as a base register, update this register's value
157
+
158
+ def deref?
159
+ self[:deref]
160
+ end
161
+
162
+ def reg
163
+ return :illegal if self[:reg] == 255
164
+ REGISTER[self[:reg]]
165
+ end
166
+
167
+ def writeback?
168
+ self[:writeback]
169
+ end
170
+ end
171
+
172
+ class StatusMask < FFI::Struct
173
+ layout :control, :bool, # control field mask (c)
174
+ :extension, :bool, # extension field mask (x)
175
+ :flags, :bool, # flags field mask (f)
176
+ :reg, :uint8, # StatusReg
177
+ :status, :bool # status field mask (s)
178
+
179
+ def control?
180
+ self[:control]
181
+ end
182
+
183
+ def extension?
184
+ self[:extension]
185
+ end
186
+
187
+ def flags?
188
+ self[:flags]
189
+ end
190
+
191
+ def reg
192
+ return :illegal if self[:reg] == 255
193
+ REGISTER[self[:reg]]
194
+ end
195
+
196
+ def status?
197
+ self[:status]
198
+ end
199
+ end
200
+
201
+ class ShiftImm < FFI::Struct
202
+ layout :imm, :uint32, # immediate shift offset
203
+ :op, :uint8 # Shift
204
+
205
+ def imm
206
+ self[:imm]
207
+ end
208
+ alias_method :value, :imm
209
+
210
+ def op
211
+ SHIFT[self[:op]]
212
+ end
213
+ end
214
+
215
+ class ShiftReg < FFI::Struct
216
+ layout :op, :uint8, # Shift
217
+ :reg, :uint8 # Register
218
+
219
+ def op
220
+ return :illegal if self[:op] == 255
221
+ SHIFT[self[:op]]
222
+ end
223
+
224
+ def reg
225
+ return :illegal if self[:reg] == 255
226
+ REGISTER[self[:reg]]
227
+ end
228
+ end
229
+
230
+ class OffsetImm < FFI::Struct
231
+ layout :post_indexed, :bool, # if true, add offset to base register and write-back AFTER derefencing base register
232
+ :value, :int32 # offset value
233
+
234
+ def post_indexed?
235
+ self[:post_indexed]
236
+ end
237
+
238
+ def value
239
+ self[:value]
240
+ end
241
+ alias_method :offset, :value
242
+ end
243
+
244
+ class OffsetReg < FFI::Struct
245
+ layout :add, :bool, # if true, add offset to base register, otherwise subtract
246
+ :post_indexed, :bool, # if true, add offset to base register and write-back AFTER derefencing base register
247
+ :reg, :uint8 # Register
248
+
249
+ def add?
250
+ self[:add]
251
+ end
252
+
253
+ def post_indexed?
254
+ self[:post_indexed]
255
+ end
256
+
257
+ def reg
258
+ return :illegal if self[:reg] == 255
259
+ REGISTER[self[:reg]]
260
+ end
261
+ end
262
+
263
+ class CpsrMode < FFI::Struct
264
+ layout :mode, :uint32, # mode bits
265
+ :writeback, :bool # writeback to base register
266
+
267
+ def mode
268
+ self[:mode]
269
+ end
270
+ alias_method :bits, :mode
271
+
272
+ def writeback?
273
+ self[:writeback]
274
+ end
275
+ end
276
+
277
+ class CpsrFlags < FFI::Struct
278
+ layout :a, :bool, # imprecise data abort
279
+ :enable, :bool, # enable the A/I/F flags if true otherwise disable
280
+ :f, :bool, # FIQ interrupt
281
+ :i, :bool # IRQ interrupt
282
+
283
+ def abort?
284
+ self[:a]
285
+ end
286
+
287
+ def enable?
288
+ self[:enable]
289
+ end
290
+
291
+ def fiq_interrupt?
292
+ self[:f]
293
+ end
294
+
295
+ def irq_interrupt?
296
+ self[:i]
297
+ end
298
+ end
299
+
300
+ class ArgumentValue < FFI::Union
301
+ layout :reg, Reg,
302
+ :reg_list, RegList,
303
+ :co_reg, :uint8,
304
+ :status_reg, :uint8,
305
+ :status_mask, StatusMask,
306
+ :shift, :uint8,
307
+ :shift_imm, ShiftImm,
308
+ :shift_reg, ShiftReg,
309
+ :u_imm, :uint32,
310
+ :sat_imm, :uint32,
311
+ :s_imm, :int32,
312
+ :offset_imm, OffsetImm,
313
+ :offset_reg, OffsetReg,
314
+ :branch_dest, :int32,
315
+ :co_option, :uint32,
316
+ :co_opcode, :uint32,
317
+ :coproc_num, :uint32,
318
+ :cpsr_mode, CpsrMode,
319
+ :cpsr_flags, CpsrFlags,
320
+ :endian, :uint8
321
+
322
+ def co_reg
323
+ CO_REG[self[:co_reg]]
324
+ end
325
+
326
+ def status_reg
327
+ STATUS_REG[self[:status_reg]]
328
+ end
329
+
330
+ def shift
331
+ SHIFT[self[:shift]]
332
+ end
333
+
334
+ def endian
335
+ return :illegal if self[:endian] == 255
336
+ ENDIAN[self[:endian]]
337
+ end
338
+ end
339
+
340
+ class Argument < FFI::Struct
341
+ layout :kind, :uint8,
342
+ :value, ArgumentValue
343
+
344
+ def kind
345
+ ARGUMENT_KIND[self[:kind]]
346
+ end
347
+
348
+ def value
349
+ raise "No value for argument of kind 'none'" if kind == :none
350
+ self[:value][kind]
351
+ end
352
+ end
353
+
354
+ class Arguments < FFI::AutoPointer
355
+ include Enumerable
356
+
357
+ def self.release(ptr)
358
+ UnarmBind.free_ins_args(ptr, 6)
359
+ end
360
+
361
+ def [](index)
362
+ raise IndexError, 'there are 6 args' if index < 0 || index >= 6
363
+ Argument.new(self + index * Argument.size)
364
+ end
365
+
366
+ def each
367
+ return enum_for(:each) unless block_given?
368
+ 6.times { |i| yield self[i] }
369
+ end
370
+
371
+ end
372
+
373
+ Arg = Argument
374
+ Args = Arguments
375
+
376
+ class CStr < FFI::AutoPointer
377
+ def self.release(ptr)
378
+ UnarmBind.free_c_str(ptr)
379
+ end
380
+
381
+ def to_s
382
+ self.read_string
383
+ end
384
+ end
385
+
386
+ end
387
+
388
+
389
+ module Unarm
390
+ extend UnarmBind
391
+
392
+ module CPU
393
+ ARM9 = :arm9 # ARMv5Te
394
+ ARM7 = :arm7 # ARMv4T
395
+ end
396
+
397
+ @cpu = CPU::ARM9
398
+ @symbols9 = nil
399
+ @symbols7 = nil
400
+ @raw_syms = {}
401
+
402
+ def self.use_arm9
403
+ @cpu = CPU::ARM9
404
+ end
405
+
406
+ def self.use_arm7
407
+ @cpu = CPU::ARM7
408
+ end
409
+
410
+ def self.cpu = @cpu
411
+
412
+ def self.symbols9 = @symbols9
413
+ def self.symbols7 = @symbols7
414
+
415
+ def self.symbols
416
+ @cpu == CPU::ARM9 ? symbols9 : symbols7
417
+ end
418
+
419
+ def self.raw_syms = @raw_syms
420
+
421
+ def self.shitty_demangle(sym)
422
+ return sym unless sym.start_with?('_Z')
423
+
424
+ sym = sym[2..]
425
+
426
+ is_vtable = false
427
+
428
+ if sym.start_with? 'NK'
429
+ sym = sym[2..]
430
+
431
+ elsif sym.start_with? 'N'
432
+ sym = sym[1..]
433
+
434
+ elsif sym.start_with? 'TV'
435
+ sym = sym[(sym[2] == 'N' ? 3 : 2)..]
436
+ is_vtable = true
437
+
438
+ end
439
+
440
+ names = []
441
+ loop do
442
+ break if sym.nil? || sym.empty? || !sym[0].match?(/\d/)
443
+ len_end = sym.index(/\D/)
444
+ n_len = Integer(sym[...len_end], exception: false)
445
+ n = sym[len_end, n_len]
446
+ names << n
447
+ sym = sym[len_end + n_len..]
448
+ end
449
+
450
+ unless sym.nil?
451
+ if sym.start_with? 'nw'
452
+ names << 'new'
453
+
454
+ elsif sym.start_with? 'na'
455
+ names << 'new[]'
456
+
457
+ elsif sym.start_with? 'dl'
458
+ names << 'delete'
459
+
460
+ elsif sym.start_with? 'da'
461
+ names << 'delete[]'
462
+
463
+ elsif sym.start_with? 'eq'
464
+ names << '=='
465
+
466
+ elsif sym.start_with? 'ne'
467
+ names << '!='
468
+
469
+ elsif sym.match? /[DC]\d/
470
+ names << sym[..1]
471
+ end
472
+ end
473
+
474
+ names.join('::') + (is_vtable ? '::vtable' : '')
475
+ end
476
+
477
+ class Symbol < FFI::Struct
478
+ layout :name, :pointer,
479
+ :addr, :uint32
480
+ end
481
+
482
+ class Symbols
483
+ attr_reader :map, :locs, :count, :demangled_map, :ambig_demangled
484
+
485
+ def self.load(file_path)
486
+ syms = {} # maps symbol names to their addresses
487
+ locs = {} # maps symbol names to their code locations (e.g. arm9, ov0, ov10)
488
+ dest = nil # current symbol location
489
+ File.open(file_path) do |f|
490
+ f.each_line do |line|
491
+ parts = line.split
492
+
493
+ next if parts.length < 3
494
+
495
+ is_comment = line.strip.start_with?('/')
496
+ if is_comment
497
+ new_dest = parts[1].split('_')
498
+ dest = new_dest[new_dest.length == 1 ? 0 : 1] if new_dest[0].include?('arm')
499
+ next
500
+ end
501
+
502
+ next if is_comment || (line.length < 4)
503
+
504
+ parts.delete_at(1) # removes '='
505
+ parts = parts[0..1] # keep symbol and name
506
+
507
+ parts[1] = parts[1].chomp(';') if parts[1].end_with?(';')
508
+
509
+ addr = parts[1].hex
510
+ next if addr <= 0
511
+ parts[1] = addr # - (addr & 1)
512
+ syms[parts[0]] = parts[1]
513
+ if dest
514
+ locs[dest] = Array.new unless locs[dest]
515
+ locs[dest] << parts[0]
516
+ end
517
+
518
+ end
519
+ end
520
+ return syms, locs
521
+ end
522
+
523
+ def initialize(args = {})
524
+ if args.has_key? :file_path
525
+ @map, @locs = Symbols.load(args[:file_path])
526
+ elsif args.has_key? :syms
527
+ @map, @locs = args[:syms].map, args[:syms].locs
528
+ else
529
+ raise ArgumentError, 'Symbols must be initialized through a file or a hash'
530
+ end
531
+
532
+ if args.has_key? :locs
533
+ all_syms = @map
534
+ @map = {}
535
+ args[:locs].each do |loc|
536
+ @locs[loc].each { |sym| @map[sym] = all_syms[sym] }
537
+ end
538
+ end
539
+
540
+ @count = @map.length
541
+
542
+ @demangled_map = {}
543
+ @ambig_demangled = []
544
+
545
+ @map.each do |sym, addr|
546
+ demangled = Unarm.shitty_demangle(sym)
547
+ if @demangled_map[demangled].nil?
548
+ @demangled_map[demangled] = sym
549
+ else
550
+ @ambig_demangled << [demangled, addr]
551
+ end
552
+ end
553
+ end
554
+
555
+ end
556
+
557
+ class RawSymbols # symbols that can be passed to Rust
558
+ attr_reader :ptr, :count
559
+
560
+ def initialize(syms)
561
+ @count = syms.count
562
+ @ptr = FFI::MemoryPointer.new(Symbol, @count)
563
+ sym_arr = @count.times.map do |i|
564
+ Symbol.new(@ptr + i*Symbol.size)
565
+ end
566
+ @name_ptrs = [] # keeps memory for symbol names alive (?)
567
+ syms.map.each_with_index do |(name, addr), i|
568
+ name_ptr = FFI::MemoryPointer.from_string(name)
569
+ @name_ptrs << name_ptr
570
+ sym_arr[i][:name] = name_ptr
571
+ sym_arr[i][:addr] = addr
572
+ end
573
+ end
574
+
575
+ end
576
+
577
+ def self.load_symbols9(file_path)
578
+ @symbols9 = Symbols.new(file_path: file_path)
579
+ end
580
+
581
+ def self.load_symbols7(file_path)
582
+ @symbols7 = Symbols.new(file_path: file_path)
583
+ end
584
+
585
+ def self.symbol_map
586
+ if @cpu == CPU::ARM9
587
+ raise 'Symbols9 not loaded' if !@symbols9
588
+ @symbols9.map
589
+ else
590
+ raise 'Symbols7 not loaded' if !@symbols7
591
+ @symbols7.map
592
+ end
593
+ end
594
+
595
+ def self.get_raw_symbols(loc)
596
+ syms = loc == 'arm7' ? @symbols7 : @symbols9
597
+ loc = 'arm9' if !syms.locs.has_key?(loc)
598
+ locs = %w[arm7 arm9].include?(loc) ? [loc] : ['arm9', loc]
599
+ @raw_syms[loc] ||= RawSymbols.new(Symbols.new(syms: syms, locs: locs))
600
+ end
601
+
602
+ class << self
603
+ alias_method :sym_map, :symbol_map
604
+ alias_method :get_raw_syms, :get_raw_symbols
605
+ end
606
+
607
+ class Ins
608
+ include UnarmBind
609
+
610
+ class << self
611
+ alias_method :disasm, :new
612
+ end
613
+
614
+ attr_reader :raw, :arguments, :address
615
+
616
+ alias_method :args, :arguments
617
+ alias_method :addr, :address
618
+
619
+ def size
620
+ @@size
621
+ end
622
+ alias_method :ins_size, :size
623
+
624
+ def string
625
+ @str.to_s
626
+ end
627
+ alias_method :str, :string
628
+
629
+ def eql?(other)
630
+ @raw == other.raw
631
+ end
632
+ alias_method :==, :eql?
633
+
634
+ def opcode
635
+ UnarmBind::OPCODE[@opcode_id]
636
+ end
637
+
638
+ def mnemonic
639
+ UnarmBind::OPCODE_MNEMONIC[@opcode_id]
640
+ end
641
+
642
+ def is_conditional?
643
+ @conditional
644
+ end
645
+ alias_method :conditional?, :is_conditional?
646
+
647
+ def is_unconditional?
648
+ !@conditional
649
+ end
650
+ alias_method :unconditional?, :is_unconditional?
651
+
652
+ def is_data_operation?
653
+ @data_op
654
+ end
655
+ alias_method :is_data_op?, :is_data_operation?
656
+
657
+ def is_illegal?
658
+ opcode == :illegal
659
+ end
660
+ alias_method :illegal?, :is_illegal?
661
+
662
+ def sets_flags?
663
+ @sets_flags
664
+ end
665
+ alias_method :updates_condition_flags?, :sets_flags?
666
+
667
+ def has_imod? # does instruction modify interrupt flags?
668
+ opcode == :cps
669
+ end
670
+
671
+ def branch_destination
672
+ arg = @arguments.find {|a| a.kind == :branch_dest}
673
+ return nil if !arg
674
+ arg.value + @address
675
+ end
676
+ alias_method :branch_dest, :branch_destination
677
+
678
+ def target_address
679
+ if opcode == :ldr && args[1].kind == :reg && args[1].value.reg == :pc && args[2].kind == :offset_imm
680
+ address + args[2].value.value + 8
681
+ else
682
+ nil
683
+ end
684
+ end
685
+ alias_method :target_addr, :target_address
686
+
687
+ def branch_to_register?
688
+ opcode == :bx ||
689
+ (mnemonic == 'mov' && args[0].value.reg == :pc) || (mnemonic == 'ldr' && args[0].value.reg == :pc)
690
+ end
691
+ alias_method :branch_to_reg?, :branch_to_register?
692
+
693
+ def function_end?
694
+ return false if conditional?
695
+
696
+ branch_to_register? ||
697
+ (mnemonic == 'pop' && args[0].value.contains?(:pc)) ||
698
+ (mnemonic == 'ldm' && args[0].value.reg == :sp && args[1].value.contains?(:pc))
699
+ end
700
+
701
+ end
702
+
703
+ class ArmIns < Ins
704
+ @@size = 4
705
+
706
+ def initialize(ins, addr = 0, loc = Unarm.cpu.to_s)
707
+ @raw = ins
708
+ @address = addr ? addr : nil
709
+ @ptr = FFI::AutoPointer.new(send(:"#{Unarm.cpu.to_s}_new_arm_ins", ins), method(:free_arm_ins))
710
+
711
+ if Unarm.symbols
712
+ syms = Unarm.get_raw_syms(loc)
713
+ @str = CStr.new(send(:"#{Unarm.cpu.to_s}_arm_ins_to_str_with_syms", @ptr, syms.ptr, syms.count, addr, 0))
714
+ else
715
+ @str = CStr.new(send(:"#{Unarm.cpu.to_s}_arm_ins_to_str", @ptr))
716
+ end
717
+
718
+ @arguments = Arguments.new(send(:"#{Unarm.cpu.to_s}_arm_ins_get_args", @ptr))
719
+ @opcode_id = send(:"#{Unarm.cpu.to_s}_arm_ins_get_opcode_id", @ptr)
720
+
721
+ @conditional = arm_ins_is_conditional(@ptr)
722
+ @data_op = arm_ins_is_data_operation(@ptr)
723
+ @sets_flags = arm_ins_updates_condition_flags(@ptr)
724
+ end
725
+
726
+ def is_compare_operation? # does opcode compare a register with another value?
727
+ [:cmn, :cmp, :teq, :tst].include? opcode
728
+ end
729
+ alias_method :is_compare_op?, :is_compare_operation?
730
+
731
+ end
732
+
733
+ class ThumbIns < Ins
734
+ @@size = 2
735
+
736
+ def initialize(ins, addr = 0, loc = Unarm.cpu.to_s)
737
+ @raw = ins
738
+ @address = addr ? addr : nil
739
+ @ptr = FFI::AutoPointer.new(send(:"#{Unarm.cpu.to_s}_new_thumb_ins", ins), method(:free_thumb_ins))
740
+
741
+ if Unarm.symbols
742
+ syms = Unarm.get_raw_syms(loc)
743
+ @str = CStr.new(send(:"#{Unarm.cpu.to_s}_thumb_ins_to_str_with_syms", @ptr, syms.ptr, syms.count, addr, 0))
744
+ else
745
+ @str = CStr.new(send(:"#{Unarm.cpu.to_s}_thumb_ins_to_str", @ptr))
746
+ end
747
+
748
+ @arguments = Arguments.new(send(:"#{Unarm.cpu.to_s}_thumb_ins_get_args", @ptr))
749
+ @opcode_id = send(:"#{Unarm.cpu.to_s}_thumb_ins_get_opcode_id", @ptr)
750
+
751
+ @conditional = thumb_ins_is_conditional(@ptr)
752
+ @data_op = thumb_ins_is_data_operation(@ptr)
753
+ @sets_flags = thumb_ins_updates_condition_flags(@ptr)
754
+ end
755
+
756
+ end
757
+
758
+ class Parser
759
+ include UnarmBind
760
+
761
+ attr_reader :mode
762
+
763
+ module Mode
764
+ ARM = 0
765
+ THUMB = 1
766
+ DATA = 2
767
+ end
768
+
769
+ module Endian
770
+ LITTLE = 0
771
+ BIG = 1
772
+ end
773
+
774
+ def set_parse_mode(mode)
775
+ raise ArgumentError, 'mode must be ARM, THUMB, or DATA' unless (Mode::ARM..Mode::DATA).include? mode
776
+ @mode = mode
777
+ end
778
+
779
+ def initialize(data_ptr, data_size, addr, mode = Mode::ARM, endian = Endian::LITTLE)
780
+ set_parse_mode(mode)
781
+ # TODO!!!!!
782
+ end
783
+
784
+ end
785
+
786
+ class Data
787
+ include UnarmBind
788
+
789
+ attr_reader :raw, :size, :address, :location, :string, :value
790
+
791
+ alias_method :addr, :address
792
+ alias_method :loc, :location
793
+ alias_method :str, :string
794
+
795
+ def get_directive(size)
796
+ case size
797
+ when 4
798
+ '.word'
799
+ when 2
800
+ '.hword'
801
+ when 1
802
+ '.byte'
803
+ else
804
+ raise "Could not determine directive from data size: #{size}"
805
+ end
806
+ end
807
+
808
+ def initialize(raw, size: 4, addr: 0, loc: Unarm.cpu.to_s, might_be_ptr: true)
809
+ @raw = raw
810
+ @address = addr
811
+ @location = loc
812
+ if raw.is_a? String
813
+ @size = raw.length + 1
814
+ @string = ".asciiz \"#{raw}\""
815
+ else
816
+ @size = size
817
+ if size == 4 && might_be_ptr
818
+ syms = Unarm.get_raw_syms(loc)
819
+ raw_str = CStr.new(get_sym_for_addr(raw, syms.ptr, syms.count))
820
+ value = raw_str.null? ? raw.to_hex : raw_str.to_s
821
+ else
822
+ value = raw.to_hex
823
+ end
824
+ @string = "#{get_directive(size)} #{value}"
825
+ @value = value
826
+ end
827
+ end
828
+
829
+ def eql?(other)
830
+ @raw == other.raw
831
+ end
832
+ alias_method :==, :eql?
833
+
834
+ end
835
+
836
+ end