From a9cfac7ff14d93ff22092366f99eb2dd3213ea7e Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Tue, 19 Dec 2023 22:39:33 -0700 Subject: Add RISC-V architecture plugin --- arch/riscv/src/lib.rs | 2570 +++++++++++++++++++++++++++++++++++++++++++ arch/riscv/src/liftcheck.rs | 427 +++++++ 2 files changed, 2997 insertions(+) create mode 100644 arch/riscv/src/lib.rs create mode 100644 arch/riscv/src/liftcheck.rs (limited to 'arch/riscv/src') diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs new file mode 100644 index 00000000..20136114 --- /dev/null +++ b/arch/riscv/src/lib.rs @@ -0,0 +1,2570 @@ +// Option -> Result +// rework operands/instruction text +// helper func for reading/writing to registers +// do the amo max/min instructions +// Platform api + +use std::borrow::Cow; +use std::fmt; +use std::marker::PhantomData; + +use binaryninja::{ + add_optional_plugin_dependency, architecture, + architecture::{ + llvm_assemble, Architecture, ArchitectureExt, CoreArchitecture, CustomArchitectureHandle, + ImplicitRegisterExtend, InstructionInfo, LlvmServicesCodeModel, LlvmServicesDialect, + LlvmServicesRelocMode, Register as Reg, RegisterInfo, UnusedFlag, UnusedRegisterStack, + UnusedRegisterStackInfo, + }, + binaryview::{BinaryView, BinaryViewExt}, + callingconvention::{register_calling_convention, CallingConventionBase, ConventionBuilder}, + custombinaryview::{BinaryViewType, BinaryViewTypeExt}, + disassembly::{InstructionTextToken, InstructionTextTokenContents}, + function::Function, + functionrecognizer::FunctionRecognizer, + llil, + llil::{ + ExprInfo, InstrInfo, Label, Liftable, LiftableWithSize, LiftedNonSSA, Lifter, Mutable, + NonSSA, + }, + rc::Ref, + relocation::{ + CoreRelocationHandler, CustomRelocationHandlerHandle, RelocationHandler, RelocationInfo, + RelocationType, + }, + string::BnString, + symbol::{Symbol, SymbolType}, + types::{max_confidence, min_confidence, Conf, NameAndType, Type}, +}; + +use riscv_dis::{ + FloatReg, FloatRegType, Instr, IntRegType, Op, RegFile, Register as RiscVRegister, + RiscVDisassembler, RoundMode, +}; + +enum RegType { + Integer(u32), + Float(u32), +} + +#[repr(u32)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +enum Intrinsic { + Uret, + Sret, + Mret, + Wfi, + Csrrw, + Csrwr, + Csrrd, + Csrrs, + Csrrc, + Fadd(u8, RoundMode), + Fsub(u8, RoundMode), + Fmul(u8, RoundMode), + Fdiv(u8, RoundMode), + Fsqrt(u8, RoundMode), + Fsgnj(u8), + Fsgnjn(u8), + Fsgnjx(u8), + Fmin(u8), + Fmax(u8), + Fclass(u8), + FcvtFToF(u8, u8, RoundMode), + FcvtIToF(u8, u8, RoundMode), + FcvtFToI(u8, u8, RoundMode), + FcvtUToF(u8, u8, RoundMode), + FcvtFToU(u8, u8, RoundMode), + Fence, +} + +#[derive(Copy, Clone)] +struct Register { + id: u32, + _dis: PhantomData, +} + +#[derive(Copy, Clone)] +struct RiscVIntrinsic { + id: Intrinsic, + _dis: PhantomData, +} + +impl Register { + fn new(id: u32) -> Self { + Self { + id, + _dis: PhantomData, + } + } + + fn reg_type(&self) -> RegType { + let int_reg_count = ::int_reg_count(); + + if self.id < int_reg_count { + RegType::Integer(self.id) + } else { + RegType::Float(self.id - int_reg_count) + } + } +} + +impl From> for Register { + fn from(reg: riscv_dis::IntReg) -> Self { + Self { + id: reg.id(), + _dis: PhantomData, + } + } +} + +impl From> for Register { + fn from(reg: FloatReg) -> Self { + let int_reg_count = ::int_reg_count(); + + Self { + id: reg.id() + int_reg_count, + _dis: PhantomData, + } + } +} + +impl Into>> for Register { + fn into(self) -> llil::Register> { + llil::Register::ArchReg(self) + } +} + +impl RegisterInfo for Register { + type RegType = Self; + + fn parent(&self) -> Option { + None + } + + fn size(&self) -> usize { + match self.reg_type() { + RegType::Integer(_) => ::Int::width(), + RegType::Float(_) => ::Float::width(), + } + } + + fn offset(&self) -> usize { + 0 + } + fn implicit_extend(&self) -> ImplicitRegisterExtend { + ImplicitRegisterExtend::NoExtend + } +} + +impl architecture::Register for Register { + type InfoType = Self; + + fn name(&self) -> Cow { + match self.reg_type() { + RegType::Integer(id) => match id { + 0 => "zero".into(), + 1 => "ra".into(), + 2 => "sp".into(), + 3 => "gp".into(), + 4 => "tp".into(), + r @ 5..=7 => format!("t{}", r - 5).into(), + r @ 8..=9 => format!("s{}", r - 8).into(), + r @ 10..=17 => format!("a{}", r - 10).into(), + r @ 18..=27 => format!("s{}", r - 16).into(), + r @ 28..=31 => format!("t{}", r - 25).into(), + _ => unreachable!(), + }, + RegType::Float(id) => match id { + r @ 0..=7 => format!("ft{}", r).into(), + r @ 8..=9 => format!("fs{}", r - 8).into(), + r @ 10..=17 => format!("fa{}", r - 10).into(), + r @ 18..=27 => format!("fs{}", r - 16).into(), + r @ 28..=31 => format!("ft{}", r - 20).into(), + _ => unreachable!(), + }, + } + } + + fn info(&self) -> Self { + *self + } + + fn id(&self) -> u32 { + self.id + } +} + +impl<'a, D: 'static + RiscVDisassembler + Send + Sync> Liftable<'a, RiscVArch> for Register { + type Result = llil::ValueExpr; + + fn lift( + il: &'a llil::Lifter>, + reg: Self, + ) -> llil::Expression<'a, RiscVArch, Mutable, NonSSA, Self::Result> { + match reg.reg_type() { + RegType::Integer(0) => il.const_int(reg.size(), 0), + RegType::Integer(_) => il.reg(reg.size(), reg), + _ => il.unimplemented(), + } + } +} + +impl<'a, D: 'static + RiscVDisassembler + Send + Sync> LiftableWithSize<'a, RiscVArch> + for Register +{ + fn lift_with_size( + il: &'a llil::Lifter>, + reg: Self, + size: usize, + ) -> llil::Expression<'a, RiscVArch, Mutable, NonSSA, llil::ValueExpr> { + #[cfg(debug_assertions)] + { + if reg.size() < size { + log::warn!( + "il @ {:x} attempted to lift {} byte register as {} byte expr", + il.current_address(), + reg.size(), + size + ); + } + } + + match reg.reg_type() { + RegType::Integer(0) => il.const_int(size, 0), + RegType::Integer(_) => { + let expr = il.reg(reg.size(), reg); + + if size < reg.size() { + il.low_part(size, expr).build() + } else { + expr + } + } + _ => il.unimplemented(), + } + } +} + +impl fmt::Debug for Register { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(self.name().as_ref()) + } +} + +impl RiscVIntrinsic { + fn id_from_parts(id: u32, sz1: Option, sz2: Option, rm: Option) -> u32 { + let sz1 = sz1.unwrap_or(0); + let sz2 = sz2.unwrap_or(0); + let rm = match rm { + None | Some(RoundMode::Dynamic) => 0, + Some(RoundMode::RoundNearestEven) => 1, + Some(RoundMode::RoundTowardZero) => 2, + Some(RoundMode::RoundDown) => 3, + Some(RoundMode::RoundUp) => 4, + Some(RoundMode::RoundMaxMagnitude) => 5, + }; + + let mut id = id << 20; + id |= sz1 as u32; + id |= (sz2 as u32) << 8; + id |= (rm as u32) << 16; + id + } + + fn parts_from_id(id: u32) -> Option<(u32, u8, u8, RoundMode)> { + let sz1 = (id & 0xff) as u8; + let sz2 = ((id >> 8) & 0xff) as u8; + let rm = match (id >> 16) & 0xf { + 0 => RoundMode::Dynamic, + 1 => RoundMode::RoundNearestEven, + 2 => RoundMode::RoundTowardZero, + 3 => RoundMode::RoundDown, + 4 => RoundMode::RoundUp, + 5 => RoundMode::RoundMaxMagnitude, + _ => return None, + }; + Some(((id >> 20) & 0xfff, sz1, sz2, rm)) + } + + fn from_id(id: u32) -> Option> { + match Self::parts_from_id(id) { + Some((0, _, _, _)) => Some(Intrinsic::Uret.into()), + Some((1, _, _, _)) => Some(Intrinsic::Sret.into()), + Some((2, _, _, _)) => Some(Intrinsic::Mret.into()), + Some((3, _, _, _)) => Some(Intrinsic::Wfi.into()), + Some((4, _, _, _)) => Some(Intrinsic::Csrrw.into()), + Some((5, _, _, _)) => Some(Intrinsic::Csrwr.into()), + Some((6, _, _, _)) => Some(Intrinsic::Csrrd.into()), + Some((7, _, _, _)) => Some(Intrinsic::Csrrs.into()), + Some((8, _, _, _)) => Some(Intrinsic::Csrrc.into()), + Some((9, size, _, rm)) => Some(Intrinsic::Fadd(size, rm).into()), + Some((10, size, _, rm)) => Some(Intrinsic::Fsub(size, rm).into()), + Some((11, size, _, rm)) => Some(Intrinsic::Fmul(size, rm).into()), + Some((12, size, _, rm)) => Some(Intrinsic::Fdiv(size, rm).into()), + Some((13, size, _, rm)) => Some(Intrinsic::Fsqrt(size, rm).into()), + Some((14, size, _, _)) => Some(Intrinsic::Fsgnj(size).into()), + Some((15, size, _, _)) => Some(Intrinsic::Fsgnjn(size).into()), + Some((16, size, _, _)) => Some(Intrinsic::Fsgnjx(size).into()), + Some((17, size, _, _)) => Some(Intrinsic::Fmin(size).into()), + Some((18, size, _, _)) => Some(Intrinsic::Fmax(size).into()), + Some((19, size, _, _)) => Some(Intrinsic::Fclass(size).into()), + Some((20, ssize, dsize, rm)) => Some(Intrinsic::FcvtFToF(ssize, dsize, rm).into()), + Some((21, isize, fsize, rm)) => Some(Intrinsic::FcvtIToF(isize, fsize, rm).into()), + Some((22, fsize, isize, rm)) => Some(Intrinsic::FcvtFToI(fsize, isize, rm).into()), + Some((23, usize, fsize, rm)) => Some(Intrinsic::FcvtUToF(usize, fsize, rm).into()), + Some((24, fsize, usize, rm)) => Some(Intrinsic::FcvtFToU(fsize, usize, rm).into()), + Some((25, _, _, _)) => Some(Intrinsic::Fence.into()), + _ => None, + } + } + + fn int_size_suffix(size: u8) -> &'static str { + match size { + 4 => "_i32", + 8 => "_i64", + _ => unreachable!(), + } + } + + fn uint_size_suffix(size: u8) -> &'static str { + match size { + 4 => "_u32", + 8 => "_u64", + _ => unreachable!(), + } + } + + fn float_size_suffix(size: u8) -> &'static str { + match size { + 4 => "_s", + 8 => "_d", + 16 => "_q", + _ => unreachable!(), + } + } + + fn round_mode_suffix(rm: RoundMode) -> &'static str { + match rm { + RoundMode::RoundNearestEven => "_rne", + RoundMode::RoundTowardZero => "_rtz", + RoundMode::RoundDown => "_rdn", + RoundMode::RoundUp => "_rup", + RoundMode::RoundMaxMagnitude => "_rmm", + RoundMode::Dynamic => "", + } + } +} + +impl From for RiscVIntrinsic { + fn from(id: Intrinsic) -> Self { + Self { + id, + _dis: PhantomData, + } + } +} + +impl architecture::Intrinsic for RiscVIntrinsic { + fn name(&self) -> Cow { + match self.id { + Intrinsic::Uret => "_uret".into(), + Intrinsic::Sret => "_sret".into(), + Intrinsic::Mret => "_mret".into(), + Intrinsic::Wfi => "_wfi".into(), + Intrinsic::Csrrw => "_csrrw".into(), + Intrinsic::Csrwr => "_csrwr".into(), + Intrinsic::Csrrd => "_csrrd".into(), + Intrinsic::Csrrs => "_csrrs".into(), + Intrinsic::Csrrc => "_csrrc".into(), + Intrinsic::Fadd(size, rm) => format!( + "_fadd{}{}", + Self::float_size_suffix(size), + Self::round_mode_suffix(rm) + ) + .into(), + Intrinsic::Fsub(size, rm) => format!( + "_fsub{}{}", + Self::float_size_suffix(size), + Self::round_mode_suffix(rm) + ) + .into(), + Intrinsic::Fmul(size, rm) => format!( + "_fmul{}{}", + Self::float_size_suffix(size), + Self::round_mode_suffix(rm) + ) + .into(), + Intrinsic::Fdiv(size, rm) => format!( + "_fdiv{}{}", + Self::float_size_suffix(size), + Self::round_mode_suffix(rm) + ) + .into(), + Intrinsic::Fsqrt(size, rm) => format!( + "_fsqrt{}{}", + Self::float_size_suffix(size), + Self::round_mode_suffix(rm) + ) + .into(), + Intrinsic::Fsgnj(size) => format!("_fsgnj{}", Self::float_size_suffix(size)).into(), + Intrinsic::Fsgnjn(size) => format!("_fsgnjn{}", Self::float_size_suffix(size)).into(), + Intrinsic::Fsgnjx(size) => format!("_fsgnjx{}", Self::float_size_suffix(size)).into(), + Intrinsic::Fmin(size) => format!("_fmin{}", Self::float_size_suffix(size)).into(), + Intrinsic::Fmax(size) => format!("_fmax{}", Self::float_size_suffix(size)).into(), + Intrinsic::Fclass(size) => format!("_fclass{}", Self::float_size_suffix(size)).into(), + Intrinsic::FcvtFToF(usize, fsize, rm) => format!( + "_fcvt{}_to{}{}", + Self::float_size_suffix(usize), + Self::float_size_suffix(fsize), + Self::round_mode_suffix(rm) + ) + .into(), + Intrinsic::FcvtIToF(isize, fsize, rm) => format!( + "_fcvt{}_to{}{}", + Self::int_size_suffix(isize), + Self::float_size_suffix(fsize), + Self::round_mode_suffix(rm) + ) + .into(), + Intrinsic::FcvtFToI(fsize, isize, rm) => format!( + "_fcvt{}_to{}{}", + Self::float_size_suffix(fsize), + Self::int_size_suffix(isize), + Self::round_mode_suffix(rm) + ) + .into(), + Intrinsic::FcvtUToF(usize, fsize, rm) => format!( + "_fcvt{}_to{}{}", + Self::uint_size_suffix(usize), + Self::float_size_suffix(fsize), + Self::round_mode_suffix(rm) + ) + .into(), + Intrinsic::FcvtFToU(fsize, usize, rm) => format!( + "_fcvt{}_to{}{}", + Self::float_size_suffix(fsize), + Self::uint_size_suffix(usize), + Self::round_mode_suffix(rm) + ) + .into(), + Intrinsic::Fence => "_fence".into(), + } + } + + fn id(&self) -> u32 { + match self.id { + Intrinsic::Uret => Self::id_from_parts(0, None, None, None), + Intrinsic::Sret => Self::id_from_parts(1, None, None, None), + Intrinsic::Mret => Self::id_from_parts(2, None, None, None), + Intrinsic::Wfi => Self::id_from_parts(3, None, None, None), + Intrinsic::Csrrw => Self::id_from_parts(4, None, None, None), + Intrinsic::Csrwr => Self::id_from_parts(5, None, None, None), + Intrinsic::Csrrd => Self::id_from_parts(6, None, None, None), + Intrinsic::Csrrs => Self::id_from_parts(7, None, None, None), + Intrinsic::Csrrc => Self::id_from_parts(8, None, None, None), + Intrinsic::Fadd(size, rm) => Self::id_from_parts(9, Some(size), None, Some(rm)), + Intrinsic::Fsub(size, rm) => Self::id_from_parts(10, Some(size), None, Some(rm)), + Intrinsic::Fmul(size, rm) => Self::id_from_parts(11, Some(size), None, Some(rm)), + Intrinsic::Fdiv(size, rm) => Self::id_from_parts(12, Some(size), None, Some(rm)), + Intrinsic::Fsqrt(size, rm) => Self::id_from_parts(13, Some(size), None, Some(rm)), + Intrinsic::Fsgnj(size) => Self::id_from_parts(14, Some(size), None, None), + Intrinsic::Fsgnjn(size) => Self::id_from_parts(15, Some(size), None, None), + Intrinsic::Fsgnjx(size) => Self::id_from_parts(16, Some(size), None, None), + Intrinsic::Fmin(size) => Self::id_from_parts(17, Some(size), None, None), + Intrinsic::Fmax(size) => Self::id_from_parts(18, Some(size), None, None), + Intrinsic::Fclass(size) => Self::id_from_parts(19, Some(size), None, None), + Intrinsic::FcvtFToF(ssize, dsize, rm) => { + Self::id_from_parts(20, Some(ssize), Some(dsize), Some(rm)) + } + Intrinsic::FcvtIToF(isize, fsize, rm) => { + Self::id_from_parts(21, Some(isize), Some(fsize), Some(rm)) + } + Intrinsic::FcvtFToI(fsize, isize, rm) => { + Self::id_from_parts(22, Some(isize), Some(fsize), Some(rm)) + } + Intrinsic::FcvtUToF(usize, fsize, rm) => { + Self::id_from_parts(23, Some(usize), Some(fsize), Some(rm)) + } + Intrinsic::FcvtFToU(fsize, usize, rm) => { + Self::id_from_parts(24, Some(usize), Some(fsize), Some(rm)) + } + Intrinsic::Fence => Self::id_from_parts(25, None, None, None), + } + } + + fn inputs(&self) -> Vec> { + match self.id { + Intrinsic::Uret | Intrinsic::Sret | Intrinsic::Mret | Intrinsic::Wfi => { + vec![] + } + Intrinsic::Csrrd => { + vec![NameAndType::new( + "csr".into(), + &Type::int(4, false), + max_confidence(), + )] + } + Intrinsic::Csrrw | Intrinsic::Csrwr | Intrinsic::Csrrs | Intrinsic::Csrrc => { + vec![ + NameAndType::new("csr".into(), &Type::int(4, false), max_confidence()), + NameAndType::new( + "value".into(), + &Type::int(::Int::width(), false), + min_confidence(), + ), + ] + } + Intrinsic::Fadd(size, _) + | Intrinsic::Fsub(size, _) + | Intrinsic::Fmul(size, _) + | Intrinsic::Fdiv(size, _) + | Intrinsic::Fsgnj(size) + | Intrinsic::Fsgnjn(size) + | Intrinsic::Fsgnjx(size) + | Intrinsic::Fmin(size) + | Intrinsic::Fmax(size) => { + vec![ + NameAndType::new("".into(), &Type::float(size as usize), max_confidence()), + NameAndType::new("".into(), &Type::float(size as usize), max_confidence()), + ] + } + Intrinsic::Fsqrt(size, _) + | Intrinsic::Fclass(size) + | Intrinsic::FcvtFToF(size, _, _) + | Intrinsic::FcvtFToI(size, _, _) + | Intrinsic::FcvtFToU(size, _, _) => { + vec![NameAndType::new( + "".into(), + &Type::float(size as usize), + max_confidence(), + )] + } + Intrinsic::FcvtIToF(size, _, _) => { + vec![NameAndType::new( + "".into(), + &Type::int(size as usize, true), + max_confidence(), + )] + } + Intrinsic::FcvtUToF(size, _, _) => { + vec![NameAndType::new( + "".into(), + &Type::int(size as usize, false), + max_confidence(), + )] + } + Intrinsic::Fence => { + vec![NameAndType::new( + "".into(), + &Type::int(4, false), + min_confidence(), + )] + } + } + } + + fn outputs(&self) -> Vec>> { + match self.id { + Intrinsic::Uret + | Intrinsic::Sret + | Intrinsic::Mret + | Intrinsic::Wfi + | Intrinsic::Csrwr + | Intrinsic::Fence => { + vec![] + } + Intrinsic::Csrrw | Intrinsic::Csrrd | Intrinsic::Csrrs | Intrinsic::Csrrc => { + vec![Conf::new( + Type::int(::Int::width(), false), + min_confidence(), + )] + } + Intrinsic::Fadd(size, _) + | Intrinsic::Fsub(size, _) + | Intrinsic::Fmul(size, _) + | Intrinsic::Fdiv(size, _) + | Intrinsic::Fsqrt(size, _) + | Intrinsic::Fsgnj(size) + | Intrinsic::Fsgnjn(size) + | Intrinsic::Fsgnjx(size) + | Intrinsic::Fmin(size) + | Intrinsic::Fmax(size) + | Intrinsic::FcvtFToF(_, size, _) + | Intrinsic::FcvtIToF(_, size, _) + | Intrinsic::FcvtUToF(_, size, _) => { + vec![Conf::new(Type::float(size as usize), max_confidence())] + } + Intrinsic::Fclass(_) => { + vec![Conf::new(Type::int(4, false), min_confidence())] + } + Intrinsic::FcvtFToI(_, size, _) => { + vec![Conf::new(Type::int(size as usize, true), max_confidence())] + } + Intrinsic::FcvtFToU(_, size, _) => { + vec![Conf::new(Type::int(size as usize, false), max_confidence())] + } + } + } +} + +struct RiscVArch { + handle: CoreArchitecture, + custom_handle: CustomArchitectureHandle>, + _dis: PhantomData, +} + +impl architecture::Architecture for RiscVArch { + type Handle = CustomArchitectureHandle; + + type RegisterInfo = Register; + type Register = Register; + type RegisterStackInfo = UnusedRegisterStackInfo; + type RegisterStack = UnusedRegisterStack; + + type Flag = UnusedFlag; + type FlagWrite = UnusedFlag; + type FlagClass = UnusedFlag; + type FlagGroup = UnusedFlag; + + type Intrinsic = RiscVIntrinsic; + + fn endianness(&self) -> binaryninja::Endianness { + binaryninja::Endianness::LittleEndian + } + + fn address_size(&self) -> usize { + ::Int::width() + } + + fn default_integer_size(&self) -> usize { + ::Int::width() + } + + fn instruction_alignment(&self) -> usize { + use riscv_dis::StandardExtension; + + if D::CompressedExtension::supported() { + 2 + } else { + 4 + } + } + + fn max_instr_len(&self) -> usize { + 4 + } + + fn opcode_display_len(&self) -> usize { + self.max_instr_len() + } + + fn associated_arch_by_addr(&self, _addr: &mut u64) -> CoreArchitecture { + self.handle + } + + fn instruction_info(&self, data: &[u8], addr: u64) -> Option { + use architecture::BranchInfo; + + let (inst_len, op) = match D::decode(addr, data) { + Ok(Instr::Rv16(op)) => (2, op), + Ok(Instr::Rv32(op)) => (4, op), + _ => return None, + }; + + let mut res = InstructionInfo::new(inst_len, false); + + match op { + Op::Jal(ref j) => { + let target = addr.wrapping_add(j.imm() as i64 as u64); + + let branch = if j.rd().id() == 0 { + BranchInfo::Unconditional(target) + } else { + BranchInfo::Call(target) + }; + + res.add_branch(branch, None); + } + Op::Jalr(ref i) => { + // TODO handle the calls with rs1 == 0? + if i.rd().id() == 0 { + let branch_type = if i.rs1().id() == 1 { + BranchInfo::FunctionReturn + } else { + BranchInfo::Unresolved + }; + + res.add_branch(branch_type, None); + } + } + Op::Beq(ref b) + | Op::Bne(ref b) + | Op::Blt(ref b) + | Op::Bge(ref b) + | Op::BltU(ref b) + | Op::BgeU(ref b) => { + res.add_branch(BranchInfo::False(addr.wrapping_add(inst_len as u64)), None); + res.add_branch( + BranchInfo::True(addr.wrapping_add(b.imm() as i64 as u64)), + None, + ); + } + Op::Ecall => { + res.add_branch(BranchInfo::SystemCall, None); + } + Op::Ebreak => { + // TODO is this valid, or should lifting handle this? + res.add_branch(BranchInfo::Unresolved, None); + } + Op::Uret | Op::Sret | Op::Mret => { + res.add_branch(BranchInfo::FunctionReturn, None); + } + _ => {} + } + + Some(res) + } + + fn instruction_text( + &self, + data: &[u8], + addr: u64, + ) -> Option<(usize, Vec)> { + use riscv_dis::Operand; + use InstructionTextTokenContents::*; + + let inst = match D::decode(addr, data) { + Ok(i) => i, + _ => return None, + }; + + let (inst_len, op) = match inst { + Instr::Rv16(op) => (2, op), + Instr::Rv32(op) => (4, op), + }; + + let mut res = Vec::new(); + let mut mnem = format!("{}", inst.mnem()); + let mut pad_len = 8usize.saturating_sub(mnem.len()); + let mut operands = inst.operands(); + + // Handle pseudo-instructions. Only single instruction pseudo-instructions are handled. + match op { + Op::AddI(i) => { + // addi zero, zero, 0 => nop + if i.rd().id() == 0 && i.rs1().id() == 0 && i.imm() == 0 { + mnem = "nop".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.clear(); + } + // addi rd, zero, imm => li rd, imm + else if i.rs1().id() == 0 { + mnem = "li".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(1); + } + // addi rd, rs, 0 => mv rd, rs + else if i.imm() == 0 { + mnem = "mv".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(2); + } + } + Op::AddIW(i) => { + // addiw rd, rs, 0 => sext.w rd, rs + if i.imm() == 0 { + mnem = "sext.w".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(2); + } + } + Op::Beq(i) => { + // beq rs, zero, offset => beqz rs, offset + if i.rs2().id() == 0 { + mnem = "beqz".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(1); + } + } + Op::Bne(i) => { + // bne rs, zero, offset => bnez rs, offset + if i.rs2().id() == 0 { + mnem = "bnez".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(1); + } + } + Op::Bge(i) => { + // bge zero, rs, offset => blez rs, offset + if i.rs1().id() == 0 { + mnem = "blez".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(0); + } + // bge rs, zero, offset => bgez rs, offset + else if i.rs2().id() == 0 { + mnem = "bgez".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(1); + } + } + Op::Blt(i) => { + // blt zero, rs, offset => bgtz rs, offset + if i.rs1().id() == 0 { + mnem = "bgtz".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(0); + } + // blt rs, zero, offset => bltz rs, offset + else if i.rs2().id() == 0 { + mnem = "bltz".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(1); + } + } + Op::Jal(i) => { + // jal zero, offset => j offset + if i.rd().id() == 0 { + mnem = "j".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(0); + } + // jal ra, offset => jal offset + else if i.rd().id() == 0 { + operands.remove(0); + } + } + Op::Jalr(i) => { + // jalr zero, ra, 0 => ret + if i.rd().id() == 0 && i.rs1().id() == 1 && i.imm() == 0 { + mnem = "ret".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.clear(); + } + // jalr zero, rs, 0 => jr rs + else if i.rd().id() == 0 && i.imm() == 0 { + mnem = "jr".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(2); + operands.remove(0); + } + // jalr ra, rs, 0 => jalr rs + else if i.rd().id() == 1 && i.imm() == 0 { + mnem = "jalr".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(2); + operands.remove(0); + } + } + Op::Slt(i) => { + // slt rd, rs, zero => sltz rd, rs + if i.rs2().id() == 0 { + mnem = "sltz".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(2); + } + // slt rd, zero, rs => sgtz rd, rs + else if i.rs1().id() == 0 { + mnem = "sgtz".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(1); + } + } + Op::SltU(i) => { + // sltu rd, zero, rs => snez rd, rs + if i.rs1().id() == 0 { + mnem = "snez".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(1); + } + } + Op::SltIU(i) => { + // sltiu rd, rs, 1 => seqz rd, rs + if i.imm() == 1 { + mnem = "seqz".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(2); + } + } + Op::Sub(i) => { + // sub rd, zero, rs => neg rd, rs + if i.rs1().id() == 0 { + mnem = "neg".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(1); + } + } + Op::SubW(i) => { + // subw rd, zero, rs => negw rd, rs + if i.rs1().id() == 0 { + mnem = "negw".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(1); + } + } + Op::XorI(i) => { + // xori rd, rs, -1 => not rd, rs + if i.imm() == -1 { + mnem = "not".into(); + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(2); + } + } + Op::Fsgnj(i) => { + // fsgnj rd, rs, rs => fmv rd, rs + if i.rs1().id() == i.rs2().id() { + mnem = match i.width() { + 4 => "fmv.s".into(), + 8 => "fmv.d".into(), + 16 => "fmv.q".into(), + _ => unreachable!(), + }; + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(2); + } + } + Op::Fsgnjn(i) => { + // fsgnjn rd, rs, rs => fneg rd, rs + if i.rs1().id() == i.rs2().id() { + mnem = match i.width() { + 4 => "fneg.s".into(), + 8 => "fneg.d".into(), + 16 => "fneg.q".into(), + _ => unreachable!(), + }; + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(2); + } + } + Op::Fsgnjx(i) => { + // fsgnjx rd, rs, rs => fabs rd, rs + if i.rs1().id() == i.rs2().id() { + mnem = match i.width() { + 4 => "fabs.s".into(), + 8 => "fabs.d".into(), + 16 => "fabs.q".into(), + _ => unreachable!(), + }; + pad_len = 8usize.saturating_sub(mnem.len()); + operands.remove(2); + } + } + _ => (), + } + + res.push(InstructionTextToken::new(BnString::new(mnem), Instruction)); + + for (i, oper) in operands.iter().enumerate() { + if i == 0 { + res.push(InstructionTextToken::new( + BnString::new(format!("{:1$}", " ", pad_len)), + Text, + )); + } else { + res.push(InstructionTextToken::new( + BnString::new(","), + OperandSeparator, + )); + res.push(InstructionTextToken::new(BnString::new(" "), Text)); + } + + match *oper { + Operand::R(r) => { + let reg = self::Register::from(r); + + res.push(InstructionTextToken::new( + BnString::new(®.name()), + Register, + )); + } + Operand::F(r) => { + let reg = self::Register::from(r); + + res.push(InstructionTextToken::new( + BnString::new(®.name()), + Register, + )); + } + Operand::I(i) => { + match op { + Op::Beq(..) + | Op::Bne(..) + | Op::Blt(..) + | Op::Bge(..) + | Op::BltU(..) + | Op::BgeU(..) + | Op::Jal(..) => { + // BRANCH or JAL + let target = addr.wrapping_add(i as i64 as u64); + + res.push(InstructionTextToken::new( + BnString::new(format!("0x{:x}", target)), + CodeRelativeAddress(target), + )); + } + _ => { + res.push(InstructionTextToken::new( + BnString::new(match i { + -0x8_0000..=-1 => format!("-0x{:x}", -i), + _ => format!("0x{:x}", i), + }), + Integer(i as u64), + )); + } + } + } + Operand::M(i, b) => { + let reg = self::Register::from(b); + + res.push(InstructionTextToken::new( + BnString::new(""), + BeginMemoryOperand, + )); + res.push(InstructionTextToken::new( + BnString::new(if i < 0 { + format!("-0x{:x}", -i) + } else { + format!("0x{:x}", i) + }), + Integer(i as u64), + )); + + res.push(InstructionTextToken::new(BnString::new("("), Text)); + res.push(InstructionTextToken::new( + BnString::new(®.name()), + Register, + )); + res.push(InstructionTextToken::new(BnString::new(")"), Text)); + res.push(InstructionTextToken::new( + BnString::new(""), + EndMemoryOperand, + )); + } + Operand::RM(r) => { + res.push(InstructionTextToken::new(BnString::new(r.name()), Register)); + } + } + } + + Some((inst_len, res)) + } + + fn instruction_llil( + &self, + data: &[u8], + addr: u64, + il: &mut llil::Lifter, + ) -> Option<(usize, bool)> { + let max_width = self.default_integer_size(); + + let (inst_len, op) = match D::decode(addr, data) { + Ok(Instr::Rv16(op)) => (2, op), + Ok(Instr::Rv32(op)) => (4, op), + _ => return None, + }; + + macro_rules! set_reg_or_append_fallback { + ($op:ident, $t:expr, $f:expr) => {{ + let rd = Register::from($op.rd()); + match rd.id { + 0 => $f.append(), + _ => il.set_reg(rd.size(), rd, $t).append(), + } + }}; + } + + macro_rules! simple_op { + ($op:ident, no_discard $f:expr) => {{ + let expr = $f; + set_reg_or_append_fallback!($op, expr, expr) + }}; + ($op:ident, $f:expr) => { + set_reg_or_append_fallback!($op, $f, il.nop()) + }; + } + + macro_rules! simple_i { + ($i:ident, $f:expr ) => {{ + let rs1 = Register::from($i.rs1()); + simple_op!($i, $f(rs1, $i.imm())) + }}; + } + + macro_rules! simple_r { + ($r:ident, $f:expr ) => {{ + let rs1 = Register::from($r.rs1()); + let rs2 = Register::from($r.rs2()); + simple_op!($r, $f(rs1, rs2)) + }}; + } + + match op { + Op::Load(l) => simple_op!(l, no_discard { + let size = l.width(); + let rs1 = Register::from(l.rs1()); + + let src_expr = il.add(max_width, rs1, l.imm()); + let load_expr = il.load(size, src_expr) + .with_source_operand(1); + + match (size < max_width, l.zx()) { + (false, _) => load_expr, + (true, true) => il.zx(max_width, load_expr).build(), + (true, false) => il.sx(max_width, load_expr).build(), + } + }), + Op::Store(s) => { + let size = s.width(); + let dest = il.add(max_width, Register::from(s.rs1()), s.imm()); + let mut src = il + .expression(Register::from(s.rs2())) + .with_source_operand(0); + + if size < max_width { + src = il.low_part(size, src).build(); + } + + il.store(size, dest, src).with_source_operand(1).append(); + } + + Op::AddI(i) => simple_i!(i, |rs1, imm| il.add(max_width, rs1, imm)), + Op::SltI(i) => simple_i!(i, |rs1, imm| il + .bool_to_int(max_width, il.cmp_slt(max_width, rs1, imm))), + Op::SltIU(i) => simple_i!(i, |rs1, imm| il + .bool_to_int(max_width, il.cmp_ult(max_width, rs1, imm))), + Op::XorI(i) => simple_i!(i, |rs1, imm| il.xor(max_width, rs1, imm)), + Op::OrI(i) => simple_i!(i, |rs1, imm| il.or(max_width, rs1, imm)), + Op::AndI(i) => simple_i!(i, |rs1, imm| il.and(max_width, rs1, imm)), + Op::SllI(i) => simple_i!(i, |rs1, imm| il.lsl(max_width, rs1, imm)), + Op::SrlI(i) => simple_i!(i, |rs1, imm| il.lsr(max_width, rs1, imm)), + Op::SraI(i) => simple_i!(i, |rs1, imm| il.asr(max_width, rs1, imm)), + + // r-type + Op::Add(r) => simple_r!(r, |rs1, rs2| il.add(max_width, rs1, rs2)), + Op::Sll(r) => simple_r!(r, |rs1, rs2| il.lsl(max_width, rs1, rs2)), + Op::Slt(r) => simple_r!(r, |rs1, rs2| il + .bool_to_int(max_width, il.cmp_slt(max_width, rs1, rs2))), + Op::SltU(r) => simple_r!(r, |rs1, rs2| il + .bool_to_int(max_width, il.cmp_ult(max_width, rs1, rs2))), + Op::Xor(r) => simple_r!(r, |rs1, rs2| il.xor(max_width, rs1, rs2)), + Op::Srl(r) => simple_r!(r, |rs1, rs2| il.lsr(max_width, rs1, rs2)), + Op::Or(r) => simple_r!(r, |rs1, rs2| il.or(max_width, rs1, rs2)), + Op::And(r) => simple_r!(r, |rs1, rs2| il.and(max_width, rs1, rs2)), + Op::Sub(r) => simple_r!(r, |rs1, rs2| il.sub(max_width, rs1, rs2)), + Op::Sra(r) => simple_r!(r, |rs1, rs2| il.asr(max_width, rs1, rs2)), + + // i-type 32-bit + Op::AddIW(i) => simple_i!(i, |rs1, imm| il.sx(max_width, il.add(4, rs1, imm))), + Op::SllIW(i) => simple_i!(i, |rs1, imm| il.sx(max_width, il.lsl(4, rs1, imm))), + Op::SrlIW(i) => simple_i!(i, |rs1, imm| il.sx(max_width, il.lsr(4, rs1, imm))), + Op::SraIW(i) => simple_i!(i, |rs1, imm| il.sx(max_width, il.asr(4, rs1, imm))), + + // r-type 32-bit + Op::AddW(r) => simple_r!(r, |rs1, rs2| il.sx(max_width, il.add(4, rs1, rs2))), + Op::SllW(r) => simple_r!(r, |rs1, rs2| il.sx(max_width, il.lsl(4, rs1, rs2))), + Op::SrlW(r) => simple_r!(r, |rs1, rs2| il.sx(max_width, il.lsr(4, rs1, rs2))), + Op::SubW(r) => simple_r!(r, |rs1, rs2| il.sx(max_width, il.sub(4, rs1, rs2))), + Op::SraW(r) => simple_r!(r, |rs1, rs2| il.sx(max_width, il.asr(4, rs1, rs2))), + + Op::Mul(r) => simple_r!(r, |rs1, rs2| il.mul(max_width, rs1, rs2)), + /* + Op::MulH(r) => + Op::MulHU(r) => + Op::MulHSU(r) => + */ + Op::Div(r) => simple_r!(r, |rs1, rs2| il.divs(max_width, rs1, rs2)), + Op::DivU(r) => simple_r!(r, |rs1, rs2| il.divu(max_width, rs1, rs2)), + Op::Rem(r) => simple_r!(r, |rs1, rs2| il.mods(max_width, rs1, rs2)), + Op::RemU(r) => simple_r!(r, |rs1, rs2| il.modu(max_width, rs1, rs2)), + + Op::MulW(r) => simple_r!(r, |rs1, rs2| il.sx(max_width, il.mul(4, rs1, rs2))), + Op::DivW(r) => simple_r!(r, |rs1, rs2| il.sx(max_width, il.divs(4, rs1, rs2))), + Op::DivUW(r) => simple_r!(r, |rs1, rs2| il.sx(max_width, il.divu(4, rs1, rs2))), + Op::RemW(r) => simple_r!(r, |rs1, rs2| il.sx(max_width, il.mods(4, rs1, rs2))), + Op::RemUW(r) => simple_r!(r, |rs1, rs2| il.sx(max_width, il.modu(4, rs1, rs2))), + + Op::Lui(u) => simple_op!(u, il.const_int(max_width, u.imm() as i64 as u64)), + Op::Auipc(u) => simple_op!(u, il.const_ptr(addr.wrapping_add(u.imm() as i64 as u64))), + + Op::Jal(j) => { + let target = addr.wrapping_add(j.imm() as i64 as u64); + + match (j.rd().id(), il.label_for_address(target)) { + (0, Some(l)) => il.goto(l), + (0, None) => il.jump(il.const_ptr(target)), + (_, _) => il.call(il.const_ptr(target)), + } + .append(); + } + Op::Jalr(i) => { + let rd = i.rd(); + let rs1 = i.rs1(); + let imm = i.imm(); + + let target = il.add(max_width, Register::from(rs1), imm).build(); + + match (rd.id(), rs1.id(), imm) { + (0, 1, 0) => il.ret(target).append(), // jalr zero, ra, 0 + (1, _, _) => il.call(target).append(), // indirect call + (0, _, _) => il.jump(target).append(), // indirect jump + (_, _, _) => { + // indirect jump with storage of next address to non-`ra` register + il.set_reg( + max_width, + Register::from(rd), + il.const_ptr(addr.wrapping_add(inst_len)), + ) + .append(); + il.jump(target).append(); + } + } + } + + Op::Beq(b) | Op::Bne(b) | Op::Blt(b) | Op::Bge(b) | Op::BltU(b) | Op::BgeU(b) => { + let left = Register::from(b.rs1()); + let right = Register::from(b.rs2()); + + let cond_expr = match op { + Op::Beq(..) => il.cmp_e(max_width, left, right), + Op::Bne(..) => il.cmp_ne(max_width, left, right), + Op::Blt(..) => il.cmp_slt(max_width, left, right), + Op::Bge(..) => il.cmp_sge(max_width, left, right), + Op::BltU(..) => il.cmp_ult(max_width, left, right), + Op::BgeU(..) => il.cmp_uge(max_width, left, right), + _ => unreachable!(), + }; + + let mut new_false: Option