// 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::hash::Hash; use std::marker::PhantomData; use binaryninja::relocation::{Relocation, RelocationHandlerExt}; 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, }, 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 Hash for Register { fn hash(&self, state: &mut H) { self.id.hash(state); } } impl PartialEq for Register { fn eq(&self, other: &Self) -> bool { self.id == other.id } } impl Eq for Register {} 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", &Type::int(4, false), max_confidence(), )] } Intrinsic::Csrrw | Intrinsic::Csrwr | Intrinsic::Csrrs | Intrinsic::Csrrc => { vec![ NameAndType::new("csr", &Type::int(4, false), max_confidence()), NameAndType::new( "value", &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("", &Type::float(size as usize), max_confidence()), NameAndType::new("", &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( "", &Type::float(size as usize), max_confidence(), )] } Intrinsic::FcvtIToF(size, _, _) => { vec![NameAndType::new( "", &Type::int(size as usize, true), max_confidence(), )] } Intrinsic::FcvtUToF(size, _, _) => { vec![NameAndType::new( "", &Type::int(size as usize, false), max_confidence(), )] } Intrinsic::Fence => { vec![NameAndType::new( "", &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, 0); 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(&mnem, Instruction)); for (i, oper) in operands.iter().enumerate() { if i == 0 { res.push(InstructionTextToken::new( &format!("{:1$}", " ", pad_len), Text, )); } else { res.push(InstructionTextToken::new( ",", OperandSeparator, )); res.push(InstructionTextToken::new(" ", Text)); } match *oper { Operand::R(r) => { let reg = self::Register::from(r); res.push(InstructionTextToken::new( ®.name(), Register, )); } Operand::F(r) => { let reg = self::Register::from(r); res.push(InstructionTextToken::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( &format!("0x{:x}", target), CodeRelativeAddress(target), )); } _ => { res.push(InstructionTextToken::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( "", BeginMemoryOperand, )); res.push(InstructionTextToken::new( &if i < 0 { format!("-0x{:x}", -i) } else { format!("0x{:x}", i) }, Integer(i as u64), )); res.push(InstructionTextToken::new("(", Brace)); res.push(InstructionTextToken::new( ®.name(), Register, )); res.push(InstructionTextToken::new(")", Brace)); res.push(InstructionTextToken::new( "", EndMemoryOperand, )); } Operand::RM(r) => { res.push(InstructionTextToken::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