mirror of
https://github.com/qilingframework/qiling.git
synced 2026-10-12 02:21:40 +00:00
@@ -6,3 +6,4 @@
|
||||
from .arch_arm import ArchARM, ArchCORTEX_M
|
||||
from .arch_intel import ArchIntel, ArchX86, ArchX64
|
||||
from .arch_mips import ArchMIPS
|
||||
from .arch_riscv import ArchRISCV, ArchRISCV64
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .arch import Arch
|
||||
|
||||
|
||||
class ArchRISCV(Arch):
|
||||
def __init__(self) -> None:
|
||||
regs = (
|
||||
'zero', 'ra', 'sp', 'gp', 'tp',
|
||||
't0', 't1', 't2', 's0', 's1',
|
||||
'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7',
|
||||
's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11',
|
||||
't3', 't4', 't5', 't6', 'pc'
|
||||
)
|
||||
|
||||
aliases = {
|
||||
's0': 'fp'
|
||||
}
|
||||
|
||||
super().__init__(regs, aliases, 4, 4)
|
||||
|
||||
def unalias(self, name: str) -> str:
|
||||
if name.startswith('x') and name[1:].isdigit():
|
||||
idx = int(name[1:])
|
||||
|
||||
xregs = (
|
||||
'zero', 'ra', 'sp', 'gp', 'tp',
|
||||
't0', 't1', 't2', 's0', 's1',
|
||||
'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7',
|
||||
's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11',
|
||||
't3', 't4', 't5', 't6'
|
||||
)
|
||||
|
||||
if idx < len(xregs):
|
||||
return xregs[idx]
|
||||
|
||||
return super().unalias(name)
|
||||
|
||||
|
||||
class ArchRISCV64(ArchRISCV):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._asize = 8
|
||||
@@ -7,10 +7,12 @@ from .branch_predictor import BranchPredictor
|
||||
from .branch_predictor_arm import BranchPredictorARM, BranchPredictorCORTEX_M
|
||||
from .branch_predictor_intel import BranchPredictorX86, BranchPredictorX64
|
||||
from .branch_predictor_mips import BranchPredictorMIPS
|
||||
from .branch_predictor_riscv import BranchPredictorRISCV, BranchPredictorRISCV64
|
||||
|
||||
__all__ = [
|
||||
'BranchPredictor',
|
||||
'BranchPredictorARM', 'BranchPredictorCORTEX_M',
|
||||
'BranchPredictorX86', 'BranchPredictorX64',
|
||||
'BranchPredictorMIPS'
|
||||
'BranchPredictorMIPS',
|
||||
'BranchPredictorRISCV', 'BranchPredictorRISCV64'
|
||||
]
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Callable, Dict, List, Optional, Set
|
||||
|
||||
from capstone import CS_OP_IMM, CS_OP_MEM, CS_OP_REG
|
||||
|
||||
from .branch_predictor import BranchPredictor, Prophecy
|
||||
from ..arch import ArchRISCV, ArchRISCV64
|
||||
from ..misc import InvalidInsn
|
||||
|
||||
|
||||
class BranchPredictorRISCV(BranchPredictor, ArchRISCV):
|
||||
"""Branch Predictor for RISC-V 32-bit.
|
||||
"""
|
||||
|
||||
stop = 'ebreak'
|
||||
xlen = 32
|
||||
supports_c_jal = True
|
||||
|
||||
def _unconditional_branches(self) -> Set[str]:
|
||||
branches = {'j', 'jal', 'jalr', 'jr', 'ret'}
|
||||
|
||||
if self.supports_c_jal:
|
||||
branches.add('c.jal')
|
||||
|
||||
return branches
|
||||
|
||||
def _normalize_mnemonic(self, mnemonic: str) -> str:
|
||||
return (mnemonic or '').lower()
|
||||
|
||||
def _signed(self, val: int) -> int:
|
||||
mask = (1 << self.xlen) - 1
|
||||
sign = 1 << (self.xlen - 1)
|
||||
|
||||
val &= mask
|
||||
return (val ^ sign) - sign
|
||||
|
||||
def _unsigned(self, val: int) -> int:
|
||||
return val & ((1 << self.xlen) - 1)
|
||||
|
||||
def predict(self) -> Prophecy:
|
||||
insn = self.disasm(self.cur_addr, True)
|
||||
|
||||
going = False
|
||||
where = 0
|
||||
|
||||
if isinstance(insn, InvalidInsn):
|
||||
return Prophecy(going, where)
|
||||
|
||||
mnemonic = self._normalize_mnemonic(insn.mnemonic)
|
||||
base = mnemonic[2:] if mnemonic.startswith('c.') else mnemonic
|
||||
operands: List[object] = list(insn.operands)
|
||||
|
||||
conditional: Dict[str, Callable[..., bool]] = {
|
||||
'beq' : lambda a, b: a == b,
|
||||
'bne' : lambda a, b: a != b,
|
||||
'blt' : lambda a, b: a < b,
|
||||
'bge' : lambda a, b: a >= b,
|
||||
'bltu': lambda a, b: a < b,
|
||||
'bgeu': lambda a, b: a >= b,
|
||||
'beqz': lambda a: a == 0,
|
||||
'bnez': lambda a: a != 0,
|
||||
'bgez': lambda a: a >= 0,
|
||||
'bltz': lambda a: a < 0,
|
||||
'bgtz': lambda a: a > 0,
|
||||
'blez': lambda a: a <= 0,
|
||||
}
|
||||
|
||||
def __read_reg(reg: int) -> Optional[int]:
|
||||
name = insn.reg_name(reg)
|
||||
|
||||
return name and self.read_reg(self.unalias(name))
|
||||
|
||||
def __parse_op(op: object) -> Optional[int]:
|
||||
if getattr(op, 'type', None) == CS_OP_REG:
|
||||
return __read_reg(op.reg)
|
||||
|
||||
if getattr(op, 'type', None) == CS_OP_IMM:
|
||||
return op.imm
|
||||
|
||||
if getattr(op, 'type', None) == CS_OP_MEM:
|
||||
mem = op.mem
|
||||
base_reg = __read_reg(mem.base) or 0
|
||||
index = __read_reg(mem.index) or 0
|
||||
return base_reg + index * mem.scale + mem.disp
|
||||
|
||||
return None
|
||||
|
||||
def __direct_target() -> Optional[int]:
|
||||
imms = [op for op in operands if getattr(op, 'type', None) == CS_OP_IMM]
|
||||
|
||||
if not imms:
|
||||
return None
|
||||
|
||||
return self.cur_addr + imms[-1].imm
|
||||
|
||||
def __indirect_target() -> Optional[int]:
|
||||
memop = next((op for op in operands if getattr(op, 'type', None) == CS_OP_MEM), None)
|
||||
if memop is not None:
|
||||
return __parse_op(memop)
|
||||
|
||||
regs = [op for op in operands if getattr(op, 'type', None) == CS_OP_REG]
|
||||
imms = [op for op in operands if getattr(op, 'type', None) == CS_OP_IMM]
|
||||
|
||||
if regs and imms:
|
||||
return (__parse_op(regs[-1]) or 0) + imms[-1].imm
|
||||
|
||||
if regs:
|
||||
return __parse_op(regs[-1])
|
||||
|
||||
return None
|
||||
|
||||
unconditional = self._unconditional_branches()
|
||||
is_unconditional = mnemonic in unconditional or base in unconditional
|
||||
|
||||
if mnemonic == 'c.jal' and not self.supports_c_jal:
|
||||
is_unconditional = False
|
||||
|
||||
if is_unconditional:
|
||||
going = True
|
||||
|
||||
if base == 'ret' and not operands:
|
||||
where = self.read_reg('ra')
|
||||
elif base in {'jalr', 'jr', 'ret'}:
|
||||
where = __indirect_target() or 0
|
||||
else:
|
||||
where = __direct_target() or 0
|
||||
|
||||
if base in {'jalr', 'jr', 'ret'}:
|
||||
where &= ~1
|
||||
|
||||
elif base in conditional:
|
||||
predicate = conditional[base]
|
||||
|
||||
if base in {'beqz', 'bnez', 'bgez', 'bltz', 'bgtz', 'blez'}:
|
||||
reg = __parse_op(operands[0]) if operands else None
|
||||
going = reg is not None and predicate(self._signed(reg))
|
||||
else:
|
||||
lhs = __parse_op(operands[0]) if len(operands) > 0 else None
|
||||
rhs = __parse_op(operands[1]) if len(operands) > 1 else None
|
||||
|
||||
if base in {'blt', 'bge'}:
|
||||
lhs = None if lhs is None else self._signed(lhs)
|
||||
rhs = None if rhs is None else self._signed(rhs)
|
||||
|
||||
if base in {'bltu', 'bgeu'}:
|
||||
lhs = None if lhs is None else self._unsigned(lhs)
|
||||
rhs = None if rhs is None else self._unsigned(rhs)
|
||||
|
||||
going = lhs is not None and rhs is not None and predicate(lhs, rhs)
|
||||
|
||||
if going:
|
||||
where = __direct_target() or 0
|
||||
|
||||
return Prophecy(going, where)
|
||||
|
||||
|
||||
class BranchPredictorRISCV64(BranchPredictorRISCV, ArchRISCV64):
|
||||
"""Branch Predictor for RISC-V 64-bit.
|
||||
"""
|
||||
|
||||
xlen = 64
|
||||
supports_c_jal = False
|
||||
@@ -11,7 +11,7 @@ from typing import TYPE_CHECKING, List, Tuple
|
||||
|
||||
from qiling.const import QL_ARCH
|
||||
from .context import Context
|
||||
from .arch import ArchCORTEX_M, ArchARM, ArchMIPS, ArchX86, ArchX64
|
||||
from .arch import ArchCORTEX_M, ArchARM, ArchMIPS, ArchRISCV, ArchRISCV64, ArchX86, ArchX64
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -26,7 +26,9 @@ def setup_command_helper(ql: Qiling):
|
||||
QL_ARCH.X8664: ArchX64,
|
||||
QL_ARCH.MIPS: ArchMIPS,
|
||||
QL_ARCH.ARM: ArchARM,
|
||||
QL_ARCH.CORTEX_M: ArchCORTEX_M
|
||||
QL_ARCH.CORTEX_M: ArchCORTEX_M,
|
||||
QL_ARCH.RISCV: ArchRISCV,
|
||||
QL_ARCH.RISCV64: ArchRISCV64,
|
||||
}
|
||||
|
||||
ret = type('CommandHelper', (CommandHelper, atypes[ql.arch.type]), {})
|
||||
|
||||
@@ -7,3 +7,4 @@ from .render import ContextRender
|
||||
from .render_intel import ContextRenderX86, ContextRenderX64
|
||||
from .render_mips import ContextRenderMIPS
|
||||
from .render_arm import ContextRenderARM, ContextRenderCORTEX_M
|
||||
from .render_riscv import ContextRenderRISCV, ContextRenderRISCV64
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
|
||||
#
|
||||
|
||||
from .render import ContextRender
|
||||
from ..arch import ArchRISCV, ArchRISCV64
|
||||
|
||||
|
||||
class ContextRenderRISCV(ContextRender, ArchRISCV):
|
||||
"""Context renderer for RISC-V architecture.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.regs_a_row = 5
|
||||
|
||||
def print_mode_info(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class ContextRenderRISCV64(ContextRenderRISCV, ArchRISCV64):
|
||||
"""Context renderer for RISC-V 64-bit architecture.
|
||||
"""
|
||||
@@ -16,7 +16,9 @@ from .render import (
|
||||
ContextRenderX64,
|
||||
ContextRenderARM,
|
||||
ContextRenderCORTEX_M,
|
||||
ContextRenderMIPS
|
||||
ContextRenderMIPS,
|
||||
ContextRenderRISCV,
|
||||
ContextRenderRISCV64,
|
||||
)
|
||||
|
||||
from .branch_predictor import (
|
||||
@@ -26,6 +28,8 @@ from .branch_predictor import (
|
||||
BranchPredictorARM,
|
||||
BranchPredictorCORTEX_M,
|
||||
BranchPredictorMIPS,
|
||||
BranchPredictorRISCV,
|
||||
BranchPredictorRISCV64,
|
||||
)
|
||||
|
||||
from .const import color, QDB_MSG
|
||||
@@ -111,13 +115,16 @@ def setup_branch_predictor(ql: Qiling) -> BranchPredictor:
|
||||
QL_ARCH.X8664: BranchPredictorX64,
|
||||
QL_ARCH.ARM: BranchPredictorARM,
|
||||
QL_ARCH.CORTEX_M: BranchPredictorCORTEX_M,
|
||||
QL_ARCH.MIPS: BranchPredictorMIPS
|
||||
QL_ARCH.MIPS: BranchPredictorMIPS,
|
||||
QL_ARCH.RISCV: BranchPredictorRISCV,
|
||||
QL_ARCH.RISCV64: BranchPredictorRISCV64,
|
||||
}
|
||||
|
||||
p = preds[ql.arch.type]
|
||||
|
||||
return p(ql)
|
||||
|
||||
|
||||
def setup_context_render(ql: Qiling, predictor: BranchPredictor) -> ContextRender:
|
||||
"""Setup context render according to arch.
|
||||
"""
|
||||
@@ -127,7 +134,9 @@ def setup_context_render(ql: Qiling, predictor: BranchPredictor) -> ContextRende
|
||||
QL_ARCH.X8664: ContextRenderX64,
|
||||
QL_ARCH.ARM: ContextRenderARM,
|
||||
QL_ARCH.CORTEX_M: ContextRenderCORTEX_M,
|
||||
QL_ARCH.MIPS: ContextRenderMIPS
|
||||
QL_ARCH.MIPS: ContextRenderMIPS,
|
||||
QL_ARCH.RISCV: ContextRenderRISCV,
|
||||
QL_ARCH.RISCV64: ContextRenderRISCV64,
|
||||
}
|
||||
|
||||
r = rends[ql.arch.type]
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
x/8xw $sp
|
||||
x/8i $pc
|
||||
s 1
|
||||
info snapshot
|
||||
p
|
||||
q
|
||||
@@ -0,0 +1,6 @@
|
||||
x/8xw $sp
|
||||
x/8i $pc
|
||||
s 1
|
||||
info snapshot
|
||||
p
|
||||
q
|
||||
@@ -53,6 +53,20 @@ class DebuggerTest(unittest.TestCase):
|
||||
r'qdb_scripts/x86.qdb'
|
||||
)
|
||||
|
||||
def test_qdb_riscv32_hello(self):
|
||||
self.__test_common(
|
||||
r'/bin/hello',
|
||||
r'../examples/rootfs/riscv32_linux',
|
||||
r'qdb_scripts/riscv32.qdb'
|
||||
)
|
||||
|
||||
def test_qdb_riscv64_hello(self):
|
||||
self.__test_common(
|
||||
r'/bin/hello',
|
||||
r'../examples/rootfs/riscv64_linux',
|
||||
r'qdb_scripts/riscv64.qdb'
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user