diff --git a/lib/metasm/metasm/cpu/msp430.rb b/lib/metasm/metasm/cpu/msp430.rb new file mode 100644 index 0000000000..78e89bcd8d --- /dev/null +++ b/lib/metasm/metasm/cpu/msp430.rb @@ -0,0 +1,8 @@ +# This file is part of Metasm, the Ruby assembly manipulation suite +# Copyright (C) 2006-2009 Yoann GUILLOT +# +# Licence is LGPL, see LICENCE in the top-level directory + + +require 'metasm/main' +require 'metasm/cpu/msp430/decode' diff --git a/lib/metasm/metasm/cpu/msp430/decode.rb b/lib/metasm/metasm/cpu/msp430/decode.rb new file mode 100644 index 0000000000..e51ca5280e --- /dev/null +++ b/lib/metasm/metasm/cpu/msp430/decode.rb @@ -0,0 +1,247 @@ +# This file is part of Metasm, the Ruby assembly manipulation suite +# Copyright (C) 2006-2010 Yoann GUILLOT +# +# Licence is LGPL, see LICENCE in the top-level directory + +require 'metasm/cpu/msp430/opcodes' +require 'metasm/decode' + +module Metasm +class MSP430 + def build_opcode_bin_mask(op) + op.bin_mask = 0 + op.fields.each_key { |f| + op.bin_mask |= @fields_mask[f] << @fields_shift[f] + } + op.bin_mask ^= 0xffff + end + + def build_bin_lookaside + lookaside = Array.new(256) { [] } + opcode_list.each { |op| + build_opcode_bin_mask op + b = (op.bin >> 8) & 255 + msk = (op.bin_mask >> 8) & 255 + + for i in b..(b | (255^msk)) + lookaside[i] << op if i & msk == b & msk + end + } + lookaside + end + + def decode_findopcode(edata) + di = DecodedInstruction.new(self) + val = edata.decode_imm(:u16, @endianness) + edata.ptr -= 2 + di.opcode = @bin_lookaside[(val >> 8) & 0xff].find { |opcode| (val & opcode.bin_mask) == opcode.bin } + di if di.opcode + end + + def decode_instr_op(edata, di) + before_ptr = edata.ptr + op = di.opcode + di.instruction.opname = op.name + val = edata.decode_imm(:u16, @endianness) + + field_val = lambda{ |f| + (val >> @fields_shift[f]) & @fields_mask[f] + } + + # must decode rs first + vals = {} + ([:rs, :rd, :r_pc] & op.args).each { |a| + mod = { :rs => :as, :rd => :ad, :r_pc => :ad }[a] + mod = :as if mod == :ad and not op.fields[mod] # addop_macro1 -> rs + ad + + if a == :r_pc + r = Reg.new(0) + else + r = Reg.new(field_val[a]) + end + + w = op.props[:byte] ? 1 : 2 + + case field_val[mod] + when 0 + if r.i == 3 and a == :rs + vals[a] = Expression[0] + else + vals[a] = r + end + when 1 + if r.i == 3 and a == :rs + vals[a] = Expression[1] + else + imm = edata.decode_imm(:u16, @endianness) + r = nil if r.i == 2 # [imm] + vals[a] = Memref.new(r, imm, w) + end + when 2 + if r.i == 3 + vals[a] = Expression[2] + elsif r.i == 2 + vals[a] = Expression[4] + else + vals[a] = Memref.new(r, 0, w) + end + when 3 + if r.i == 3 + vals[a] = Expression[-1] + elsif r.i == 2 + vals[a] = Expression[8] + elsif r.i == 0 # pc++ + # XXX order wrt other edata.decode_imm ? + vals[a] = Expression[edata.decode_imm(:u16, @endianness)] + else + vals[a] = Memref.new(r, 0, w, true) + end + end + } + + op.args.each { |a| + di.instruction.args << case a + when :joff; Expression[2 * Expression.make_signed(field_val[a], 10)] + when :rs, :rd, :r_pc; vals[a] + else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}" + end + } + + di.bin_length += edata.ptr - before_ptr + + return if edata.ptr > edata.length + + di + end + + def decode_instr_interpret(di, addr) + if di.opcode.props[:setip] and di.opcode.name =~ /^j/ + delta = di.instruction.args.last.reduce + arg = Expression[[addr, :+, di.bin_length], :+, delta].reduce + di.instruction.args[-1] = Expression[arg] + end + + di + end + + def backtrace_binding + @backtrace_binding ||= init_backtrace_binding + end + + def init_backtrace_binding + @backtrace_binding ||= {} + + opcode_list.map { |ol| ol.name }.uniq.each { |op| + @backtrace_binding[op] ||= case op + when 'mov'; lambda { |di, a0, a1| { a0 => Expression[a1] }} + when 'cmp', 'test'; lambda { |di, *a| {} } # TODO + when 'add', 'adc' ; lambda { |di, a0, a1| { a0 => Expression[a0, :+, a1] } } + when 'sub', 'sbc'; lambda { |di, a0, a1| { a0 => Expression[a0, :-, a1] } } + when 'and'; lambda { |di, a0, a1| { a0 => Expression[a0, :&, a1] } } + when 'or'; lambda { |di, a0, a1| { a0 => Expression[a0, :|, a1] } } + when 'xor'; lambda { |di, a0, a1| { a0 => Expression[a0, :^, a1] } } + when 'push'; lambda { |di, a0| { Indirection[:sp, 2] => Expression[a0], + :sp => Expression[:sp, :-, 2] } } + when 'call'; lambda { |di, a0| { Indirection[:sp, 2] => Expression[di.next_addr], + :sp => Expression[:sp, :-, 2] } } + when 'pop'; lambda { |di, a0| { a0 => Expression[Indirection[:sp, 2]], + :sp => Expression[:sp, :+, 2] } } + when 'ret'; lambda { |di| { :sp => Expression[:sp, :+, 2] } } + when 'reti'; lambda { |di| { :sp => Expression[:sp, :+, 4] } } + when /^j/; lambda { |di, a0| {} } + end + } + + @backtrace_binding + end + + def get_backtrace_binding(di) + a = di.instruction.args.map { |arg| + case arg + when Reg; arg.symbolic + when Memref; arg.symbolic(di.address) + else arg + end + } + + if binding = backtrace_binding[di.opcode.basename] + bd = binding[di, *a] || {} + di.instruction.args.grep(Memref).each { |m| + next unless r = m.base and m.postincr + r = m.base.symbolic + bd[r] ||= Expression[r, :+, m.size] + } + bd + else + puts "unhandled instruction to backtrace: #{di}" if $VERBOSE + { :incomplete_binding => Expression[1] } + end + end + + def get_xrefs_x(dasm, di) + return [] if not di.opcode.props[:setip] + + case di.instruction.opname + when 'ret' + return [Indirection[:sp, 2, di.address]] + when 'reti' + return [Indirection[[:sp, :+, 2], 2, di.address]] + end + + # XXX add pc, 42 ? + val = di.instruction.args[0] + case val + when Reg; val = val.symbolic + when Memref; val = val.symbolic(di.address) + end + + [Expression[val]] + end + + def backtrace_is_function_return(expr, di=nil) + expr = Expression[expr].reduce_rec + expr.kind_of?(Indirection) and expr.len == 2 and expr.target == Expression[:sp] + end + + # updates the function backtrace_binding + # if the function is big and no specific register is given, do nothing (the binding will be lazily updated later, on demand) + def backtrace_update_function_binding(dasm, faddr, f, retaddrlist, *wantregs) + b = f.backtrace_binding + + bt_val = lambda { |r| + next if not retaddrlist + b[r] = Expression::Unknown + bt = [] + retaddrlist.each { |retaddr| + bt |= dasm.backtrace(Expression[r], retaddr, :include_start => true, + :snapshot_addr => faddr, :origin => retaddr) + } + if bt.length != 1 + b[r] = Expression::Unknown + else + b[r] = bt.first + end + } + + if not wantregs.empty? + wantregs.each(&bt_val) + else + bt_val[:sp] + end + + b + end + + def replace_instr_arg_immediate(i, old, new) + i.args.map! { |a| + case a + when Expression; a == old ? new : Expression[a.bind(old => new).reduce] + when Memref + a.base = (a.base == old ? new : Expression[a.base.bind(old => new).reduce]) if a.base.kind_of?(Expression) + a + else a + end + } + end +end +end diff --git a/lib/metasm/metasm/cpu/msp430/main.rb b/lib/metasm/metasm/cpu/msp430/main.rb new file mode 100644 index 0000000000..b82ec2a9ec --- /dev/null +++ b/lib/metasm/metasm/cpu/msp430/main.rb @@ -0,0 +1,62 @@ +# This file is part of Metasm, the Ruby assembly manipulation suite +# Copyright (C) 2006-2010 Yoann GUILLOT +# +# Licence is LGPL, see LICENCE in the top-level directory + +require 'metasm/main' + +module Metasm + +class MSP430 < CPU + def initialize(e = :little) + super() + @endianness = e + @size = 16 + end + + class Reg + include Renderable + Sym = (4..15).inject(0 => :pc, 1 => :sp, 2 => :flags, 3 => :rzero) { |h, i| h.update i => "r#{i}".to_sym } + + attr_accessor :i + def initialize(i) ; @i = i end + def symbolic ; Sym[@i] end + def render ; [Sym[@i].to_s] end + def ==(o) ; o.class == self.class and o.i == @i end + end + + class Memref + attr_accessor :base, :offset, :size, :postincr + + def initialize(base, offset = 0, size = nil, postincr = false) + @base = base + @offset = Expression[offset] + @size = size + @postincr = postincr + end + + def symbolic(orig=nil) + r = @base.symbolic if @base + e = Expression[r, :+, @offset].reduce + Indirection[e, (@size || 1), orig] + end + + include Renderable + + def render + b = @base + b = @base.to_s + '++' if @base and @postincr + p = Expression[b, :+, @offset].reduce + Indirection[p, @size].render + end + end + + def init_opcode_list + init + end + + def dbg_register_list + @dbg_register_list ||= Reg::Sym.sort.transpose.last + end +end +end diff --git a/lib/metasm/metasm/cpu/msp430/opcodes.rb b/lib/metasm/metasm/cpu/msp430/opcodes.rb new file mode 100644 index 0000000000..59402047a4 --- /dev/null +++ b/lib/metasm/metasm/cpu/msp430/opcodes.rb @@ -0,0 +1,101 @@ +# This file is part of Metasm, the Ruby assembly manipulation suite +# Copyright (C) 2006-2010 Yoann GUILLOT +# +# Licence is LGPL, see LICENCE in the top-level directory + +require 'metasm/cpu/msp430/main' + +module Metasm +class MSP430 + def addop(name, bin, *args) + o = Opcode.new name, bin + + args.each { |a| + o.args << a if @valid_args[a] + o.props[a] = true if @valid_props[a] + o.fields[a] = [@fields_mask[a], @fields_shift[a]] if @fields_mask[a] + } + + @opcode_list << o + end + + def init + @opcode_list = [] + + @fields_mask = { + :as => 3, # adressing mode + :ad => 1, # adressing mode + :rd => 0xf, + :rs => 0xf, + :joff => 0x3ff, # signed offset for jumps + } + @fields_shift = { + :as => 4, + :ad => 7, + :rd => 0, + :rs => 8, + :joff => 0, + } + @valid_args = { :r_pc => true, :rd => true, :rs => true, :joff => true } + @valid_props = { :setip => true, :stopexec => true, :saveip => true, :byte => true } + + # https://en.wikipedia.org/wiki/TI_MSP430 + + addop_macro1 'rrc', 0, :byte + addop_macro1 'swpb', 1 + addop_macro1 'rra', 2, :byte + addop_macro1 'sxt', 3 + addop_macro1 'push', 4, :byte + addop_macro1 'call', 5, :setip, :stopexec, :saveip + + addop 'reti', 0b000100_110_0000000 + + addop_macro2 'jnz', 0 + addop_macro2 'jz', 1 + addop_macro2 'jnc', 2 + addop_macro2 'jc', 3 + addop_macro2 'jb', 4 # 'jn' jump if negative => jl unsigned ? + addop_macro2 'jge', 5 + addop_macro2 'jl', 6 + addop_macro2 'jmp', 7, :stopexec + + addop 'ret', 0x4130, :setip, :stopexec # mov pc, [sp++] + addop 'pop', 0x4130, :rd, :ad # mov rd, [sp++] + + addop_macro3 'mov', 4 + addop_macro3 'add', 5 + addop_macro3 'adc', 6 # 'addc' + addop_macro3 'sbc', 7 + addop_macro3 'sub', 8 + addop_macro3 'cmp', 9 + addop_macro3 'dadd',10 # decimal add with carry + addop_macro3 'test',11 # 'bit' + addop_macro3 'andn',12 # 'bic' + addop_macro3 'or', 13 # 'bis' + addop_macro3 'xor', 14 + addop_macro3 'and', 15 + end + + def addop_macro1(name, bin, *props) + if props.delete :byte + addop_byte name, (0b000100 << 10) | (bin << 7), :as, :rd, *props + else + addop name, (0b000100 << 10) | (bin << 7), :as, :rd, *props + end + end + + def addop_macro2(name, bin, *props) + addop name, (0b001 << 13) | (bin << 10), :joff, :setip, *props + end + + def addop_macro3(name, bin, *props) + addop_byte name, (bin << 12), :r_pc, :ad, :as, :rs, :setip, :stopexec # dst == pc + addop_byte name, (bin << 12), :rd, :ad, :as, :rs + end + + def addop_byte(name, bin, *props) + addop name, bin, *props + addop name + '.b', bin | (1 << 6), :byte, *props + end +end +end