diff --git a/qiling/cc/__init__.py b/qiling/cc/__init__.py index ef1cbdb0d..7d69ed49a 100644 --- a/qiling/cc/__init__.py +++ b/qiling/cc/__init__.py @@ -70,6 +70,26 @@ def setReturnValue(self, val: int) -> None: raise NotImplementedError + def setReturnAddress(self, addr: int) -> None: + """Set function return address. + + Args: + addr: return address to set + """ + + raise NotImplementedError + + def reserve(self, nslots: int) -> None: + """Reserve slots for function arguments. + + This may be used to stage a new frame before executing a native function. + + Args: + nslots: number of arg slots to reserve + """ + + raise NotImplementedError + def unwind(self, nslots: int) -> int: """Unwind frame and return from function call. @@ -149,3 +169,11 @@ def getReturnValue(self) -> int: def setReturnValue(self, value: int) -> None: self.ql.reg.write(self._retreg, value) + + def reserve(self, nslots: int) -> None: + assert nslots < len(self._argregs), 'too many slots' + + # count how many slots should be reserved on the stack + si = self._argregs[:nslots].count(None) + + self.ql.reg.arch_sp -= (self._shadow + si) * self._asize diff --git a/qiling/cc/arm.py b/qiling/cc/arm.py index 330c84322..3d0aaa254 100644 --- a/qiling/cc/arm.py +++ b/qiling/cc/arm.py @@ -11,30 +11,31 @@ from qiling import Qiling from . import QlCommonBaseCC -class aarch64(QlCommonBaseCC): - _argregs = (UC_ARM64_REG_X0, UC_ARM64_REG_X1, UC_ARM64_REG_X2, UC_ARM64_REG_X3, UC_ARM64_REG_X4, UC_ARM64_REG_X5, UC_ARM64_REG_X6, UC_ARM64_REG_X7) + (None, ) * 8 - - def __init__(self, ql: Qiling) -> None: - super().__init__(ql, UC_ARM64_REG_X0) +class QlArmBaseCC(QlCommonBaseCC): + """Calling convention base class for ARM-based systems. + Supports arguments passing over registers and stack. + """ @staticmethod def getNumSlots(argbits: int) -> int: return 1 + def setReturnAddress(self, addr: int) -> None: + # TODO: do we need to update LR? + self.ql.arch.stack_push(addr) + def unwind(self, nslots: int) -> int: # TODO: cleanup? return self.ql.arch.stack_pop() -class aarch32(QlCommonBaseCC): - _argregs = (UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3) + (None, ) * 12 +class aarch64(QlArmBaseCC): + _argregs = (UC_ARM64_REG_X0, UC_ARM64_REG_X1, UC_ARM64_REG_X2, UC_ARM64_REG_X3, UC_ARM64_REG_X4, UC_ARM64_REG_X5, UC_ARM64_REG_X6, UC_ARM64_REG_X7) + (None, ) * 8 def __init__(self, ql: Qiling) -> None: - super().__init__(ql, UC_ARM_REG_R0) + super().__init__(ql, UC_ARM64_REG_X0) - @staticmethod - def getNumSlots(argbits: int) -> int: - return 1 +class aarch32(QlArmBaseCC): + _argregs = (UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3) + (None, ) * 12 - def unwind(self, nslots: int) -> int: - # TODO: cleanup? - return self.ql.arch.stack_pop() + def __init__(self, ql: Qiling) -> None: + super().__init__(ql, UC_ARM_REG_R0) diff --git a/qiling/cc/intel.py b/qiling/cc/intel.py index c047ab781..9d90ab54e 100644 --- a/qiling/cc/intel.py +++ b/qiling/cc/intel.py @@ -25,6 +25,9 @@ def __init__(self, ql: Qiling): super().__init__(ql, retreg) + def setReturnAddress(self, addr: int) -> None: + self.ql.arch.stack_push(addr) + def unwind(self, nslots: int) -> int: # no cleanup; just pop out the return address return self.ql.arch.stack_pop() diff --git a/qiling/os/fcall.py b/qiling/os/fcall.py index 89c90728e..1a4cfd54b 100644 --- a/qiling/os/fcall.py +++ b/qiling/os/fcall.py @@ -173,3 +173,25 @@ def call(self, func: CallHook, proto: Mapping[str, Any], params: Mapping[str, An retaddr = -1 if passthru else self.cc.unwind(nslots) return targs, retval, retaddr + + def call_native(self, addr: int, args: Sequence[Tuple[Any, int]], ret: Optional[int]) -> None: + """Call a native function after properly staging its arguments and return address. + + Args: + addr: function entry point + args: a sequence of 2-tuples containing parameters types and values to pass to the function; may be empty + ret: return address; may be None + """ + + # reserve slots for arguments + nslots = self.__count_slots(atype for atype, _ in args) + self.cc.reserve(nslots) + + # set arguments values + self.writeParams(args) + + if ret is not None: + self.cc.setReturnAddress(ret) + + # call + self.ql.reg.arch_pc = addr