indis-arm 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|