#!/usr/bin/env python3
# 
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework

from unicorn.x86_const import (
	UC_X86_REG_EAX,	UC_X86_REG_RAX, UC_X86_REG_RCX, UC_X86_REG_RDI,
	UC_X86_REG_RDX,	UC_X86_REG_RSI,	UC_X86_REG_R8, UC_X86_REG_R9,
	UC_X86_REG_R10
)

from qiling.cc import QlCommonBaseCC

class QlIntelBaseCC(QlCommonBaseCC):
	"""Calling convention base class for Intel-based systems.
	Supports arguments passing over registers and stack.
	"""

	def setReturnAddress(self, addr: int) -> None:
		self.arch.stack_push(addr)

	def unwind(self, nslots: int) -> int:
		# no cleanup; just pop out the return address
		return self.arch.stack_pop()

class QlIntel64(QlIntelBaseCC):
	"""Calling convention base class for Intel-based 64-bit systems.
	"""

	_retreg = UC_X86_REG_RAX

	@staticmethod
	def getNumSlots(argbits: int) -> int:
		return max(argbits, 64) // 64

class QlIntel32(QlIntelBaseCC):
	"""Calling convention base class for Intel-based 32-bit systems.
	"""

	_retreg = UC_X86_REG_EAX

	@staticmethod
	def getNumSlots(argbits: int) -> int:
		return max(argbits, 32) // 32

	def getRawParam(self, slot: int, nbits: int = 0) -> int:
		__super_getparam = super().getRawParam

		if nbits == 64:
			lo = __super_getparam(slot)
			hi = __super_getparam(slot + 1)

			val = (hi << 32) | lo
		else:
			val = __super_getparam(slot, nbits)

		return val

class amd64(QlIntel64):
	"""Default calling convention for POSIX (x86-64).
	First 6 arguments are passed in regs, the rest are passed on the stack.
	"""

	_argregs = (UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX, UC_X86_REG_R10, UC_X86_REG_R8, UC_X86_REG_R9) + (None, ) * 10

class ms64(QlIntel64):
	"""Default calling convention for Windows and UEFI (x86-64).
	First 4 arguments are passed in regs, the rest are passed on the stack.

	Each stack frame starts with a shadow space in size of 4 items, corresponding
	to the first arguments passed in regs.
	"""

	_argregs = (UC_X86_REG_RCX, UC_X86_REG_RDX, UC_X86_REG_R8, UC_X86_REG_R9) + (None, ) * 12
	_shadow = 4

class macosx64(QlIntel64):
	"""Default calling convention for Mac OS (x86-64).
	First 6 arguments are passed in regs, the rest are passed on the stack.
	"""

	_argregs = (UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX, UC_X86_REG_RCX, UC_X86_REG_R8, UC_X86_REG_R9) + (None, ) * 10

class cdecl(QlIntel32):
	"""Calling convention used by all operating systems (x86).
	All arguments are passed on the stack.

	The caller is resopnsible to unwind the stack.
	"""

	_argregs = (None, ) * 16

class stdcall(QlIntel32):
	"""Calling convention used by all operating systems (x86).
	All arguments are passed on the stack.

	The callee is resopnsible to unwind the stack.
	"""

	_argregs = (None, ) * 16

	def unwind(self, nslots: int) -> int:
		retaddr = super().unwind(nslots)

		self.arch.regs.arch_sp += (nslots * self._asize)

		return retaddr
