#    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/cpu/arm64/opcodes'
require 'metasm/decode'

module Metasm
class ARM64
	# create the bin_mask for a given opcode
	def build_opcode_bin_mask(op)
		# bit = 0 if can be mutated by an field value, 1 if fixed by opcode
		op.bin_mask = 0
		op.fields.each { |k, (m, s)|
			op.bin_mask |= m << s
		}
		op.bin_mask = 0xffffffff ^ op.bin_mask
	end

	# create the lookaside hash from the first byte of the opcode
	def build_bin_lookaside
		lookaside = Array.new(256) { [] }

		opcode_list.each { |op|
			build_opcode_bin_mask op

			b   = (op.bin >> 24) & 0xff
			msk = (op.bin_mask >> 24) & 0xff
			b &= msk

			for i in b..(b | (255^msk))
				lookaside[i] << op if i & msk == b
			end
		}

		lookaside
	end

	def decode_findopcode(edata)
		return if edata.ptr+4 > edata.length
		di = DecodedInstruction.new(self)
		val = edata.decode_imm(:u32, @endianness)
		di.raw_data = val
		di if di.opcode = @bin_lookaside[(val >> 24) & 0xff].find { |op|
			(op.bin & op.bin_mask) == (val & op.bin_mask)
		}
	end

	def disassembler_default_func
		df = DecodedFunction.new
		df
	end

	def decode_instr_op(edata, di)
		op = di.opcode
		di.instruction.opname = op.name
		val = di.raw_data

		field_val = lambda { |f|
			(val >> @fields_shift[f]) & @fields_mask[f]
		}

		op.args.each { |a|
			di.instruction.args << case a
			when :rn, :rt, :rt2, :rm
				nr = field_val[a]
				nr = 32 if nr == 31 and op.props[:r_z]
				Reg.new nr, (op.props[:r_32] ? 32 : 64)
			when :rm_lsl_i6, :rm_lsr_i6, :rm_asr_i6, :rm_lsl_i5, :rm_lsr_i5, :rm_asr_i5
				nr = field_val[:rm]
				nr = 32 if nr == 31 and op.props[:r_z]
				r = Reg.new nr, (op.props[:r_32] ? 32 : 64)
				shift = field_val[:i6_10]
				mode = { :rm_lsl_i6 => :lsl, :rm_lsl_i5 => :lsl,
					 :rm_lsr_i6 => :lsr, :rm_lsr_i5 => :lsr,
					 :rm_asr_i6 => :asr, :rm_asr_i5 => :asr }[a]
				RegShift.new r, mode, shift
			when :m_rm_extend, :rm_extend_i3
				nr = field_val[:rm]
				nr = 32 if nr == 31
				x = field_val[:regextend_13]
				case a
				when :m_rm_extend
					shift = 0	# field_val[:i1_12] -- bug in arm doc ?
					mode = [ :resv000, :resv001, :uxtw, :lsl, :resv100, :resv101, :sxtw, :sxtx ][x]
					rm = RegShift.new Reg.new(nr, 64), mode, shift

					rn = Reg.new field_val[:rn], 64

					mem_sz = op.props[:mem_sz] || (op.props[:r_32] ? 4 : 8)
					Memref.new(rn, rm, 1, nil, mem_sz)
				when :rm_extend_i3
					r = Reg.new nr, (op.props[:r_32] ? 32 : 64)
					shift = field_val[:i3_10]
					mode = [ :uxtb, :uxth, :uxtw, :uxtx, :sxtb, :sxth, :sxtw, :sxtx ][x]
					RegShift.new r, mode, shift
				end
			when :i16_5; Expression[field_val[a]]
			when :il18_5;
				v = field_val[a]
				s = (v >> 16) & 3
				v = v & 0xffff
				Expression[v, :<<, (16*s)]
			when :i19_5; Expression[Expression.make_signed(field_val[a], 19) << 2]
			when :i26_0; Expression[Expression.make_signed(field_val[a], 26) << 2]
			when :i12_10_s1
				f = field_val[a]
				f = (f & 0xfff) << 12 if (f >> 12) & 1 == 1
				Expression[f]
			when :i19_5_2_29
				Expression.make_signed((field_val[:i19_5] << 2) | field_val[:i2_29], 21)
			when :bitmask, :bitmask_imm
				n = field_val[:bitmask_n]
				s = field_val[:bitmask_s]
				r = field_val[:bitmask_r]
				# highestsetbit stuff
				levels = ((n << 6) | (s ^ 0x3f)) >> 1
				levels = levels | (levels >> 1)
				levels = levels | (levels >> 2)
				levels = levels | (levels >> 4)
				esize = levels + 1
				s &= levels
				r &= levels
				welem = (1 << (s+1)) - 1
				# ROR(welem, r)
				wmask = ((welem >> (r % esize)) | (welem << (esize - (r % esize))))
				wmask &= (1 << esize) - 1
				# duplicate(wmask, sz)
				while esize < 64
					wmask |= wmask << esize
					esize *= 2
				end
				wmask &= (1 << di.instruction.args[0].sz) - 1
				Expression[wmask]

			when :m_rn_s9, :m_rn_u12, :m_rn_s7
				r = Reg.new(field_val[:rn], 64)
				o = case a
				    when :m_rn_s9; Expression.make_signed(field_val[:s9_12], 9)
				    when :m_rn_s7; Expression.make_signed(field_val[:s7_15], 7)
				    when :m_rn_u12; field_val[:u12_10]
				    else raise SyntaxError, "Internal error #{a.inspect} in #{op.name}"
				    end
				mem_sz = op.props[:mem_sz] || (op.props[:r_32] ? 4 : 8)
				Memref.new(r, nil, nil, Expression[o*mem_sz], mem_sz, op.props[:mem_incr])
			when :cond_12
				RegCC.new OP_CC[field_val[a]]
			else raise SyntaxError, "Internal error: invalid argument #{a.inspect} in #{op.name}"
			end
		}

		di.bin_length = 4
		di
	end

	def decode_instr_interpret(di, addr)
		if di.opcode.props[:setip] and di.instruction.args.last.kind_of?(Expression)
			di.instruction.args[-1] = Expression[Expression[addr, :+, di.instruction.args[-1]].reduce]
		elsif di.opcode.props[:pcrel]
			di.instruction.args[-1] = Expression[Expression[addr, :+, di.instruction.args[-1]].reduce]
		elsif di.opcode.props[:pcrel_page]
			di.instruction.args[-1] = Expression[Expression[[addr, :&, ~0xfff], :+, [di.instruction.args[-1], :<<, 12]].reduce]
		end
		di
	end

	def init_backtrace_binding
		@backtrace_binding ||= {}

		opcode_list.map { |ol| ol.basename }.uniq.sort.each { |op|
			binding = case op
			when 'mov', 'adr', 'adrp'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
			when 'movz'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
			when 'movn'; lambda { |di, a0, a1| { a0 => Expression[:~, a1] } }
			#when 'movk'; lambda { |di, a0, a1| a1 + lsl replace target bits of a0, other unchanged
			when 'and', 'ands', 'orr', 'or', 'eor', 'xor'
				bin_op = { 'and' => :&, 'ands' => :&, 'orr' => :|,
					'or' => :|, 'eor' => :^, 'xor' => :^ }[op]
				lambda { |di, a0, a1, a2| { a0 => Expression[ a1, bin_op, a2 ] } }
			when 'orn', 'eorn', 'bic', 'bics', 'andn'
				bin_op = { 'orn' => :|, 'eorn' => :^, 'andn' => :&, 'bic' => :&, 'bics' => :& }[op]
				lambda { |di, a0, a1, a2| { a0 => Expression[ a1, bin_op, [ :~, a2 ] ] } }
			when 'add', 'adds', 'sub', 'subs'
				bin_op = { 'add' => :+, 'adds' => :+, 'sub' => :-, 'subs' => :- }[op]
				lambda { |di, a0, a1, a2| { a0 => Expression[ a1, bin_op, a2 ] } }
			when 'ldr', 'ldrb', 'ldrsw'; lambda { |di, a0, a1| { a0 => Expression[a1] } }
			when 'str', 'strb', 'strsw'; lambda { |di, a0, a1| { a1 => Expression[a0] } }
			when 'stp'; lambda { |di, a0, a1, a2| ptr = a2.target
				{ Indirection[ptr, 8] => Expression[a0], Indirection[Expression[ptr, :+, 8].reduce, 8] => Expression[a1] } }
			when 'ldp'; lambda { |di, a0, a1, a2| ptr = a2.target
				{ a0 => Indirection[ptr, 8], a1 => Indirection[Expression[ptr, :+, 8].reduce, 8] } }
			when 'ret'; lambda { |di, *a| { } }
			when 'bl', 'blr'; lambda { |di, *a| { :x30 => Expression[di.next_addr] } }
			when 'cbz', 'cbnz', 'cmp', /^b/; lambda { |di, *a| {} }
			end

			# pre/post-increment memref done in def get_backtrace_binding(di)

			@backtrace_binding[op] ||= binding
		}

		@backtrace_binding
	end

	def get_backtrace_binding(di)
		a = di.instruction.args.map { |arg|
			case arg
			when Reg, RegShift, RegCC; arg.symbolic
			when Memref; arg.symbolic(di)
			else arg
			end
		}

		if binding = backtrace_binding[di.opcode.name]
			bd = binding[di, *a] || {}

			# handle pre-increment / post-increment memrefs
			di.instruction.args.grep(Memref).each { |m|
				next unless r = m.base and r.kind_of?(Reg)
				case m.incr
				when :pre
					# for di "str x1, [sp+10]!" ; bt_bind should be { sp += 10, [sp] = x1 } but memref.symbolic returns [sp+10]
					# eg: str x30, [sp-20]! ; ldr x30, [sp], 20 ; ret  should backtrace as  x30 -> [sp] -!> x30
					rs = r.symbolic
					bd.dup.each_key { |k|
						if k.kind_of?(Indirection)
							bd[Indirection[k.target.bind(rs => Expression[rs, :-, m.offset]).reduce, k.len, k.origin]] = bd.delete(k)
						end
					}
					bd[rs] ||= Expression[rs, :+, m.offset]
				when :post
					bd[r.symbolic] ||= Expression[r.symbolic, :+, m.offset]
				end
			}

			# handle subregisters (x30 -> w30)
			bd.keys.grep(Expression).each { |e|
				# must be Expression[reg, :&, 0xffff_ffff]
				if e.op == :& and e.rexpr == 0xffff_ffff
					reg = e.lexpr
					next if not reg.kind_of? Symbol
					val = bd.delete e
					bd[reg] = Expression[[reg, :&, 0xffff_ffff_0000_0000], :|, [val, :&, 0xffff_ffff]]
				end
			}

			bd
		else
			puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
			# assume nothing except the 1st arg is modified
			case a[0]
			when Indirection, Symbol; { a[0] => Expression::Unknown }
			when Expression; (x = a[0].externals.first) ? { x => Expression::Unknown } : {}
			else {}
			end.update(:incomplete_binding => Expression[1])
		end
	end

	def get_xrefs_x(dasm, di)
		if di.opcode.props[:setip]
			tg = di.instruction.args.last
			case tg
			when nil
				raise 'internal error: no jmp target' if di.opcode.name != 'ret'
				tg = :x30
			when Expression
			else tg = tg.symbolic(di)
			end
			[tg]
		else
			# TODO ldr pc, ..
			[]
		end
	end

	def backtrace_is_function_return(expr, di=nil)
		expr.reduce_rec == :x30
	end

	def backtrace_is_stack_address(expr)
		Expression[expr].expr_externals.include? :sp
	end
end
end
