indis-arm 0.3.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.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +8 -0
- data/Gemfile.ci +7 -0
- data/LICENSE +674 -0
- data/README.md +5 -0
- data/Rakefile +9 -0
- data/indis-arm.gemspec +22 -0
- data/lib/indis-arm/analyzer/bl_analyzer.rb +20 -0
- data/lib/indis-arm/analyzer/ldr_lit_analyzer.rb +25 -0
- data/lib/indis-arm/analyzer.rb +17 -0
- data/lib/indis-arm/arm7.inst.rb +415 -0
- data/lib/indis-arm/code_parser.rb +109 -0
- data/lib/indis-arm/cpu_state.rb +34 -0
- data/lib/indis-arm/instruction.rb +107 -0
- data/lib/indis-arm/instruction_helper.rb +156 -0
- data/lib/indis-arm/instruction_loader.rb +160 -0
- data/lib/indis-arm/version.rb +23 -0
- data/lib/indis-arm.rb +28 -0
- data/spec/fixtures/single-object.o +0 -0
- data/spec/indis-arm/code_parser_spec.rb +48 -0
- data/spec/indis-arm/instruction_helper_spec.rb +29 -0
- data/spec/indis-arm/instruction_loader_spec.rb +76 -0
- data/spec/spec_helper.rb +8 -0
- metadata +125 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
require 'ostruct'
|
20
|
+
require 'indis-arm/instruction_helper'
|
21
|
+
require 'indis-core/entity'
|
22
|
+
|
23
|
+
module Indis
|
24
|
+
module ARM
|
25
|
+
|
26
|
+
# ARM::Instruction is a code {Indis::Entity entity} that represens an ARM
|
27
|
+
# instruction
|
28
|
+
class Instruction < Indis::Entity
|
29
|
+
# Instructions can have different value formats. In such a case, +value_format+
|
30
|
+
# specifies the format to use
|
31
|
+
# @return [Symbol]
|
32
|
+
attr_accessor :value_format
|
33
|
+
|
34
|
+
# @param [Fixnum] vmaddr virtual address
|
35
|
+
# @param [Fixnum] bytes bytes that represent an instruction
|
36
|
+
def initialize(vmaddr, bytes)
|
37
|
+
super vmaddr
|
38
|
+
@size = 4
|
39
|
+
m = self.class.kmap(bytes)
|
40
|
+
self.class.process_automap.each { |p| self.instance_exec(m, &p) } if self.class.process_automap
|
41
|
+
self.instance_exec(m, &self.class.process_block) if self.class.process_block
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Indis::ARM::InstructionHelper] helper that provides common methods for DSL
|
45
|
+
def h
|
46
|
+
InstructionHelper
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
s = self.instance_eval "\"#{self.class.formats[:operator]}\""
|
51
|
+
if @value_format
|
52
|
+
fmt = self.class.formats[@value_format]
|
53
|
+
v = self.instance_eval "\"#{fmt}\""
|
54
|
+
else
|
55
|
+
v = self.instance_eval "\"#{self.class.formats[:value]}\"" if self.class.formats[:value]
|
56
|
+
end
|
57
|
+
s = "#{s}\t#{v}" if v
|
58
|
+
s
|
59
|
+
end
|
60
|
+
|
61
|
+
class << self
|
62
|
+
attr_reader :name # @return [String] instruction name
|
63
|
+
|
64
|
+
attr_reader :encoding # @return [String] instruction encoding per ARMARM
|
65
|
+
|
66
|
+
attr_reader :process_block # @return [Proc] data-processing proc
|
67
|
+
|
68
|
+
attr_reader :formats # @return [Hash] output formats
|
69
|
+
|
70
|
+
attr_reader :process_automap # @return [Array] a list of automapping procs
|
71
|
+
|
72
|
+
# @return [String] instruction mask bits
|
73
|
+
def bits_mask
|
74
|
+
@bits.gsub('0', '1').gsub(/[^1]/, '0').to_i(2)
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [String] instruction matching bits
|
78
|
+
def bits_match
|
79
|
+
@bits.gsub(/[^01]/, '0').to_i(2)
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [OpenStruct] a map of known fields to instruction value
|
83
|
+
def kmap(v)
|
84
|
+
return OpenStruct.new unless @kmap
|
85
|
+
|
86
|
+
map = @kmap.inject({}) do |map, (m, o, n)|
|
87
|
+
map[n] = (v & m) >> o
|
88
|
+
map
|
89
|
+
end
|
90
|
+
OpenStruct.new(map)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# UnknownInstruction represents an unknown (not yet mapped in DSL) instruction
|
96
|
+
class UnknownInstruction < Instruction
|
97
|
+
def initialize(vmaddr, bytes)
|
98
|
+
super
|
99
|
+
@val = bytes
|
100
|
+
end
|
101
|
+
|
102
|
+
def to_s
|
103
|
+
"UNK\t#{@val.to_s(16).upcase}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
require 'indis-core/binaryops_string'
|
20
|
+
|
21
|
+
module Indis
|
22
|
+
module ARM
|
23
|
+
module PseudoCodeInstructionHelper
|
24
|
+
def ARMExpandImm(bits_imm12) # A5.2.4
|
25
|
+
ARMExpandImm_C(bits_imm12, 0)[0] # FIXME APSR.C ???
|
26
|
+
end
|
27
|
+
|
28
|
+
def ARMExpandImm_C(bits_imm12, carry_in) # A5.2.4
|
29
|
+
unrotated_value = bits_imm12.bits(7, 0).zero_extend(32)
|
30
|
+
Shift_C(unrotated_value, :SRType_ROR, 2*(bits_imm12.bits(11, 8).to_i), carry_in)
|
31
|
+
end
|
32
|
+
|
33
|
+
def DecodeImmShift(bits2_type, bits5_imm5)
|
34
|
+
imm = bits5_imm5.to_i
|
35
|
+
case bits2_type.to_i
|
36
|
+
when 0b00
|
37
|
+
[:SRType_LSL, imm]
|
38
|
+
when 0b01
|
39
|
+
[:SRType_LSR, imm == 0 ? 32 : imm]
|
40
|
+
when 0b10
|
41
|
+
[:SRType_ASR, imm == 0 ? 32 : imm]
|
42
|
+
when 0b11
|
43
|
+
imm == 0 ? [:SRType_RRX, 1] : [:SRType_ROR, imm]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def ZeroExtend(bits_x, i) # P5.3
|
48
|
+
bits_x.zero_extend(i)
|
49
|
+
end
|
50
|
+
|
51
|
+
def SignExtend(bits_x, i) # P5.3
|
52
|
+
bits_x.sign_extend(i)
|
53
|
+
end
|
54
|
+
|
55
|
+
def Shift_C(bits_value, type, amount, carry_in) # A8.4.3
|
56
|
+
raise ArgumentError unless !(type == :SRType_RRX && amount != 1)
|
57
|
+
|
58
|
+
return [bits_value, carry_in] if amount == 0
|
59
|
+
|
60
|
+
case type
|
61
|
+
when :SRType_LSL
|
62
|
+
LSL_C(bits_value, amount)
|
63
|
+
when :SRType_LSR
|
64
|
+
LSR_C(bits_value, amount)
|
65
|
+
when :SRType_ASR
|
66
|
+
ASR_C(bits_value, amount)
|
67
|
+
when :SRType_ROR
|
68
|
+
ROR_C(bits_value, amount)
|
69
|
+
when :SRType_RRX
|
70
|
+
RRX_C(bits_value, carry_in)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def ROR_C(bits_x, shift) # A2.2.1
|
75
|
+
raise ArgumentError unless shift != 0
|
76
|
+
|
77
|
+
[bits_x.ror(shift), bits_x.rbit(0)]
|
78
|
+
end
|
79
|
+
|
80
|
+
def LSR(bits_x, shift) # A2.2.1
|
81
|
+
raise ArgumentError unless shift >= 0
|
82
|
+
bits_x >> shift
|
83
|
+
end
|
84
|
+
|
85
|
+
def LSR_C(bits_x, shift) # A2.2.1
|
86
|
+
raise ArgumentError unless shift > 0
|
87
|
+
[bits_x >> shift, bits_x.bit(shift-1)]
|
88
|
+
end
|
89
|
+
|
90
|
+
def LSL(bits_x, shift) # A2.2.1
|
91
|
+
raise ArgumentError unless shift >= 0
|
92
|
+
bits_x << shift
|
93
|
+
end
|
94
|
+
|
95
|
+
def LSL_C(bits_x, shift) # A2.2.1
|
96
|
+
raise ArgumentError unless shift > 0
|
97
|
+
[bits_x << shift, bits_x.bit(shift)]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
module InstructionHelper
|
102
|
+
class << self
|
103
|
+
COND = [".EQ", ".NE", ".CS", ".CC", ".MI", ".PL", ".VS", ".VC", ".HI", ".LS", ".GE", ".LT", ".GT", ".LE", ""]
|
104
|
+
COND_SYM = [:eq, :ne, :cs, :cc, :mi, :pl, :vs, :vc, :hi, :ls, :ge, :lt, :gt, :le, :al]
|
105
|
+
NAMED_REG = { r13: :sp, r14: :lr, r15: :pc }
|
106
|
+
REG = [:r0, :r1, :r2, :r3, :r4, :r5, :r6, :r7, :r8, :r9, :r10, :r11, :r12, :r13, :r14, :r15]
|
107
|
+
SHIFT_TYPES = {
|
108
|
+
SRType_LSL: :lsl,
|
109
|
+
SRType_LSR: :lsr,
|
110
|
+
SRType_ASR: :asr,
|
111
|
+
SRType_ROR: :ror,
|
112
|
+
SRType_RRX: :rrx,
|
113
|
+
}
|
114
|
+
|
115
|
+
def regs_from_bits(bits_list)
|
116
|
+
bl = bits_list.to_s(2)
|
117
|
+
bl = ('0'*(16-bl.length)) + bl
|
118
|
+
regs = []
|
119
|
+
bl.reverse! # XXX so that r0 is bit 0
|
120
|
+
bl.length.times do |i|
|
121
|
+
regs << "r#{i}".to_sym if bl[i] == '1'
|
122
|
+
end
|
123
|
+
regs
|
124
|
+
end
|
125
|
+
|
126
|
+
def shift_type_to_s(shift)
|
127
|
+
SHIFT_TYPES[shift]
|
128
|
+
end
|
129
|
+
|
130
|
+
def cond_to_s(cond)
|
131
|
+
('.' + cond.to_s.upcase).sub('.AL', '')
|
132
|
+
end
|
133
|
+
|
134
|
+
def regs_to_s(regs)
|
135
|
+
regs = regs.map { |r| NAMED_REG[r] || r }
|
136
|
+
regs.join(', ')
|
137
|
+
end
|
138
|
+
|
139
|
+
def flag(f, fn)
|
140
|
+
f ? fn : ''
|
141
|
+
end
|
142
|
+
|
143
|
+
def reg(i)
|
144
|
+
r = REG[i]
|
145
|
+
NAMED_REG[r] || r
|
146
|
+
end
|
147
|
+
|
148
|
+
def cond(i)
|
149
|
+
COND_SYM[i]
|
150
|
+
end
|
151
|
+
|
152
|
+
include PseudoCodeInstructionHelper
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
require 'singleton'
|
20
|
+
require 'indis-arm/instruction'
|
21
|
+
|
22
|
+
module Indis
|
23
|
+
module ARM
|
24
|
+
|
25
|
+
class NotThisInstructionError < RuntimeError; end
|
26
|
+
class UnpredictableError < RuntimeError; end
|
27
|
+
|
28
|
+
# InstructionLoader is a DSL parser for arm7.inst.rb DSL.
|
29
|
+
# The DSL is evaluated in the context of InstructionLoader singleton, the
|
30
|
+
# only top-level command is {Indis::ARM::InstructionLoader#instruction}
|
31
|
+
class InstructionLoader
|
32
|
+
include Singleton
|
33
|
+
|
34
|
+
# Loads and processes the DSL
|
35
|
+
# @return [Array] all mapped classes from DSL
|
36
|
+
def load
|
37
|
+
return @classes if @classes
|
38
|
+
|
39
|
+
@classes = []
|
40
|
+
instr_file = File.join(File.dirname(__FILE__), 'arm7.inst.rb')
|
41
|
+
self.instance_eval open(instr_file).read, instr_file, 1
|
42
|
+
|
43
|
+
@classes
|
44
|
+
end
|
45
|
+
|
46
|
+
# Loads a named instruction. The block is evaluated in a context of
|
47
|
+
# newly-created {Indis::ARM::EncodingLoader}.
|
48
|
+
# @param [Symbol] name instruction name
|
49
|
+
def instruction(name, &block)
|
50
|
+
el = EncodingLoader.new(name, @classes)
|
51
|
+
el.instance_eval(&block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class EncodingLoader
|
56
|
+
def initialize(name, arr)
|
57
|
+
@name = name
|
58
|
+
@classes = arr
|
59
|
+
end
|
60
|
+
|
61
|
+
# Loads a specific instruction encoding. The block is evaluated in a
|
62
|
+
# context of newly-created {Indis::ARM::MnemonicLoader}.
|
63
|
+
# @param [Symbol] enc instruction encoding name
|
64
|
+
def encoding(enc, &block)
|
65
|
+
unless block_given?
|
66
|
+
#puts "Encoding #{enc} of #{@name} is undefined yet"
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
name = @name
|
71
|
+
klass = Class.new(Indis::ARM::Instruction) { @name = name; @encoding = enc }
|
72
|
+
ARM.const_set("#{name}Instruction_#{enc}", klass)
|
73
|
+
|
74
|
+
ml = MnemonicLoader.new(klass)
|
75
|
+
ml.instance_eval(&block)
|
76
|
+
|
77
|
+
@classes << klass
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class MnemonicLoader
|
82
|
+
# A default map. Would set up the mapping only if the attribute name
|
83
|
+
# was passed in {Indis::ARM::MnemonicLoader#attrs}
|
84
|
+
AUTOMAPPED = {
|
85
|
+
'C' => :cond,
|
86
|
+
'n' => :Rn,
|
87
|
+
'm' => :Rm,
|
88
|
+
'd' => :Rd,
|
89
|
+
't' => :Rt,
|
90
|
+
'S' => :setflags,
|
91
|
+
}
|
92
|
+
|
93
|
+
AUTOMAPPED_PROCS = {
|
94
|
+
cond: proc { |k| @cond = h.cond(k.cond) },
|
95
|
+
Rn: proc { |k| @Rn = h.reg(k.Rn) },
|
96
|
+
Rm: proc { |k| @Rm = h.reg(k.Rm) },
|
97
|
+
Rd: proc { |k| @Rd = h.reg(k.Rd) },
|
98
|
+
Rt: proc { |k| @Rt = h.reg(k.Rt) },
|
99
|
+
setflags: proc { |k| @setflags = k.setflags == 1 },
|
100
|
+
}
|
101
|
+
|
102
|
+
def initialize(klass)
|
103
|
+
@klass = klass
|
104
|
+
end
|
105
|
+
|
106
|
+
# Loads bit representation of instruction
|
107
|
+
# @overload bits(bitstring)
|
108
|
+
# @param [String] bitstring a string with no special-mapped bits (either no specail bits at all, or all bits are automapped)
|
109
|
+
# @overload bits(bitstring, map)
|
110
|
+
# @param [String] bitstring a string with bits map
|
111
|
+
# @param [Hash{String => Symbol}] map a map from bit string to parsed symbol
|
112
|
+
def bits(*args)
|
113
|
+
raise "Malformed bits field" if args.length < 1 || args.length > 2
|
114
|
+
b = args.first
|
115
|
+
@klass.instance_eval { @bits = b }
|
116
|
+
|
117
|
+
map = {}
|
118
|
+
AUTOMAPPED.each { |k,v| map[k] = v if @klass_attrs.include?(v) }
|
119
|
+
map.merge!(args[1]) if args.length == 2
|
120
|
+
|
121
|
+
a = map.map do |v, name|
|
122
|
+
# TODO: chek on what happens if there is no cond 'C'
|
123
|
+
mask = b.gsub(Regexp.new("[^#{v}]"), '0').gsub(/[^0]/, "1")
|
124
|
+
ofs = mask.match(/0*$/)[0].length
|
125
|
+
mask = mask.to_i(2)
|
126
|
+
|
127
|
+
[mask, ofs, name]
|
128
|
+
end
|
129
|
+
|
130
|
+
@klass.instance_eval { @kmap = a }
|
131
|
+
end
|
132
|
+
|
133
|
+
# Loads instruction attributes
|
134
|
+
def attrs(*attrs)
|
135
|
+
@klass_attrs = attrs
|
136
|
+
@klass.instance_eval { attr_reader *attrs }
|
137
|
+
end
|
138
|
+
|
139
|
+
# Loads instruction format
|
140
|
+
def format(fmt)
|
141
|
+
if @klass_attrs.include?(:setflags)
|
142
|
+
formats = { operator: '#{self.class.name}#{h.flag @setflags, "S"}#{h.cond_to_s(@cond)}' }
|
143
|
+
else
|
144
|
+
formats = { operator: '#{self.class.name}#{h.cond_to_s(@cond)}' }
|
145
|
+
end
|
146
|
+
formats.merge!(fmt)
|
147
|
+
@klass.instance_eval { @formats = formats }
|
148
|
+
end
|
149
|
+
|
150
|
+
# Loads instruction processing block
|
151
|
+
def process(&block)
|
152
|
+
automap_keys = @klass_attrs & AUTOMAPPED.values
|
153
|
+
automap_proc = AUTOMAPPED_PROCS.map { |k,p| automap_keys.include?(k) ? p : nil }.compact
|
154
|
+
@klass.instance_eval { @process_automap = automap_proc }
|
155
|
+
@klass.instance_eval { @process_block = block }
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
module Indis
|
20
|
+
module ARM
|
21
|
+
VERSION = "0.3.0"
|
22
|
+
end
|
23
|
+
end
|
data/lib/indis-arm.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
##############################################################################
|
2
|
+
# Indis framework #
|
3
|
+
# Copyright (C) 2012 Vladimir "Farcaller" Pouzanov <farcaller@gmail.com> #
|
4
|
+
# #
|
5
|
+
# This program is free software: you can redistribute it and/or modify #
|
6
|
+
# it under the terms of the GNU General Public License as published by #
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or #
|
8
|
+
# (at your option) any later version. #
|
9
|
+
# #
|
10
|
+
# This program is distributed in the hope that it will be useful, #
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
13
|
+
# GNU General Public License for more details. #
|
14
|
+
# #
|
15
|
+
# You should have received a copy of the GNU General Public License #
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
17
|
+
##############################################################################
|
18
|
+
|
19
|
+
require 'indis-arm/version'
|
20
|
+
require 'indis-arm/code_parser'
|
21
|
+
require 'indis-arm/analyzer'
|
22
|
+
|
23
|
+
module Indis
|
24
|
+
# Indis::ARM provides instruction parser for ARM and Thumb instruction sets.
|
25
|
+
module ARM
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
Binary file
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'indis-core/target'
|
2
|
+
require 'indis-macho'
|
3
|
+
require 'indis-arm/code_parser'
|
4
|
+
|
5
|
+
describe Indis::ARM::CodeParser do
|
6
|
+
it "should decode arm instructions in given section" do
|
7
|
+
t = Indis::Target.new('spec/fixtures/single-object.o')
|
8
|
+
t.load
|
9
|
+
|
10
|
+
section = t.segments.find { |seg| seg.name == '__TEXT' }.sections.find { |sect| sect.name == '__text' }
|
11
|
+
|
12
|
+
code_parser = Indis::ARM::CodeParser.new(t)
|
13
|
+
|
14
|
+
code_parser.reparse_section(section)
|
15
|
+
|
16
|
+
sm = t.vmmap[section.to_vmrange]
|
17
|
+
|
18
|
+
sm.each_with_index do |b, idx|
|
19
|
+
break if idx >= section.vmsize
|
20
|
+
|
21
|
+
if idx % 4 == 0
|
22
|
+
b.should be_a(Indis::Entity)
|
23
|
+
b.should be_a(Indis::ARM::Instruction)
|
24
|
+
else
|
25
|
+
b.should be_nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should parse known instructions" do
|
31
|
+
code_parser = Indis::ARM::CodeParser.new(double("Target", vmmap: double("VMMap")))
|
32
|
+
|
33
|
+
i = code_parser.instance_eval { build_instruction(0, 0xe92d4080) }
|
34
|
+
i.should_not be_a(Indis::ARM::UnknownInstruction)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should load instructions" do
|
38
|
+
Indis::ARM::CodeParser.load_instructions
|
39
|
+
expect { Indis::ARM.const_get('PUSHInstruction_A1') }.not_to raise_error
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should honor NotThisInstructionError" do
|
43
|
+
code_parser = Indis::ARM::CodeParser.new(double("Target", vmmap: double("VMMap")))
|
44
|
+
|
45
|
+
i = code_parser.instance_eval { build_instruction(0, 0xe59f0024) }
|
46
|
+
i.class.should == Indis::ARM::LDRInstruction_A1_lit
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'indis-arm/instruction_helper'
|
2
|
+
|
3
|
+
describe Indis::ARM::InstructionHelper do
|
4
|
+
it "should decode registers" do
|
5
|
+
b = '0000111100000010'.reverse.to_i(2)
|
6
|
+
r = Indis::ARM::InstructionHelper.regs_from_bits(b)
|
7
|
+
r.should == [:r4, :r5, :r6, :r7, :r14]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Indis::ARM::PseudoCodeInstructionHelper do
|
12
|
+
it "should perform ZeroExtend" do
|
13
|
+
Indis::ARM::InstructionHelper.ZeroExtend('1101'.to_bo, 6).to_s.should == '001101'
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should perform LSR" do
|
17
|
+
a = Indis::ARM::InstructionHelper.LSR_C('101100'.to_bo, 3)
|
18
|
+
a[0].to_s.should == '000101'
|
19
|
+
a[1].should == 1
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should perform Shift_C" do
|
23
|
+
Indis::ARM::InstructionHelper.Shift_C('101100'.to_bo, :SRType_ROR, 2, 0).map{|v|v.to_s}.should == ['001011', '1']
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should perform ARMExpandImm_C" do
|
27
|
+
Indis::ARM::InstructionHelper.ARMExpandImm('000000001100'.to_bo).to_i.should == 12
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'indis-arm/instruction_loader'
|
2
|
+
|
3
|
+
describe Indis::ARM::InstructionLoader do
|
4
|
+
before(:all) { Indis::ARM::InstructionLoader.instance.load }
|
5
|
+
|
6
|
+
it "should load instructions" do
|
7
|
+
expect { Indis::ARM.const_get('PUSHInstruction_A1') }.not_to raise_error
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should parse bitmap to mask and match" do
|
11
|
+
Indis::ARM::PUSHInstruction_A1.bits_mask.should == 0xfff0000
|
12
|
+
Indis::ARM::PUSHInstruction_A1.bits_match.should == 0x92d0000
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should create attr_readers for fields" do
|
16
|
+
i = Indis::ARM::PUSHInstruction_A1.new(0, 0xe92d4080)
|
17
|
+
expect { i.regs }.not_to raise_error(NoMethodError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should make a keymap for passed values" do
|
21
|
+
m = Indis::ARM::PUSHInstruction_A1.kmap(0xe92d4080)
|
22
|
+
m.cond.should == 0xe
|
23
|
+
m.regs_list.should == 0x4080
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should eval process block and set ivars" do
|
27
|
+
i = Indis::ARM::PUSHInstruction_A1.new(0, 0xe92d4080)
|
28
|
+
i.regs.should == [:r7, :r14]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should set instruction name" do
|
32
|
+
Indis::ARM::PUSHInstruction_A1.name.should == :PUSH
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should set instruction encoding" do
|
36
|
+
Indis::ARM::PUSHInstruction_A1.encoding.should == :A1
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should raise NotThisInstructionError if the other mnemonic should be searched for" do
|
40
|
+
expect { Indis::ARM::LDRInstruction_A1_imm.new(0, 0xe59f0024) }.to raise_error(Indis::ARM::NotThisInstructionError)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should provide full mnemonic when asked to_s" do
|
44
|
+
Indis::ARM::PUSHInstruction_A1.new(0, 0xe92d4080).to_s.should == "PUSH\t{r7, lr}"
|
45
|
+
Indis::ARM::MOVInstruction_A1_reg.new(0, 0xe1a0700d).to_s.should == "MOV\tr7, sp"
|
46
|
+
Indis::ARM::SUBInstruction_A1_spimm.new(0, 0xe24dd00c).to_s.should == "SUB\tsp, sp, #12"
|
47
|
+
Indis::ARM::STRInstruction_A1_imm.new(0, 0xe5070004).to_s.should == "STR\tr0, [r7, #-4]"
|
48
|
+
Indis::ARM::STRInstruction_A1_imm.new(0, 0xe58d1004).to_s.should == "STR\tr1, [sp, #4]"
|
49
|
+
Indis::ARM::LDRInstruction_A1_imm.new(0, 0xe5171004).to_s.should == "LDR\tr1, [r7, #-4]"
|
50
|
+
Indis::ARM::LDRInstruction_A1_imm.new(0, 0xe59d2004).to_s.should == "LDR\tr2, [sp, #4]"
|
51
|
+
Indis::ARM::LDRInstruction_A1_lit.new(0, 0xe59f0024).to_s.should == "LDR\tr0, [pc, #36]"
|
52
|
+
Indis::ARM::LDRInstruction_A1_reg.new(0, 0xe79f1001).to_s.should == "LDR\tr1, [pc, r1]"
|
53
|
+
Indis::ARM::ADDInstruction_A1_imm.new(0, 0xe2804001).to_s.should == "ADD\tr4, r0, #1"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should provide cond when there is 'C' in bits" do
|
57
|
+
i = Indis::ARM::PUSHInstruction_A1.new(0, 0xe92d4080)
|
58
|
+
i.cond.should == :al
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should not provide cond when there is no 'C' in bits" do
|
62
|
+
i = Indis::ARM::DMBInstruction_A1.new(0, 0xf57ff05f)
|
63
|
+
expect { i.cond }.to raise_error(NoMethodError)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should provide Rx when there are 'n', 'd', 'm' in bits" do
|
67
|
+
i = Indis::ARM::MOVInstruction_A1_reg.new(0, 0xe1a0700d)
|
68
|
+
i.Rd.should == :r7
|
69
|
+
i.Rm.should == :sp
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should not provide Rx when there are no 'n', 'd', 'm' in bits" do
|
73
|
+
i = Indis::ARM::MOVInstruction_A1_reg.new(0, 0xe1a0700d)
|
74
|
+
expect { i.Rn }.to raise_error(NoMethodError)
|
75
|
+
end
|
76
|
+
end
|