diff --git a/arch/riscv/disasm/src/lib.rs b/arch/riscv/disasm/src/lib.rs index c35e76d379..d19ebf220c 100644 --- a/arch/riscv/disasm/src/lib.rs +++ b/arch/riscv/disasm/src/lib.rs @@ -187,6 +187,55 @@ pub enum Op { FmvToInt(FpMvToIntInst), FmvFromInt(FpMvFromIntInst), Fclass(FpClassInst), + + // Zbs (bit-manipulation, single-bit) + Bclr(RTypeIntInst), + BclrI(ITypeIntInst), + Bext(RTypeIntInst), + BextI(ITypeIntInst), + Binv(RTypeIntInst), + BinvI(ITypeIntInst), + Bset(RTypeIntInst), + BsetI(ITypeIntInst), + + // Zba (bit-manipulation, address generation) + ShXAdd(u8, RTypeIntInst), + ShXAddUW(u8, RTypeIntInst), + AddUW(RTypeIntInst), + SllIUW(ITypeIntInst), + + // Zbb (bit-manipulation, "basic") + Andn(RTypeIntInst), + Orn(RTypeIntInst), + Xnor(RTypeIntInst), + + Max(RTypeIntInst), + MaxU(RTypeIntInst), + Min(RTypeIntInst), + MinU(RTypeIntInst), + + SextB(ITypeIntInst), + SextH(ITypeIntInst), + ZextH(RTypeIntInst), + + Rol(RTypeIntInst), + Ror(RTypeIntInst), + RorI(ITypeIntInst), + RolW(RTypeIntInst), + RorW(RTypeIntInst), + RorIW(ITypeIntInst), + + Clz(ITypeIntInst), + ClzW(ITypeIntInst), + Ctz(ITypeIntInst), + CtzW(ITypeIntInst), + Cpop(ITypeIntInst), + CpopW(ITypeIntInst), + Orcb(ITypeIntInst), + Rev8(ITypeIntInst), + + // WCH + WchMcpy(RTypeIntInst), // does not use R-format, but uses three registers } pub trait Register { @@ -320,6 +369,13 @@ impl RegFile for Rv32ERegs { } } +#[derive(Copy, Clone, Debug)] +pub struct Rv32FRegs; +impl RegFile for Rv32FRegs { + type Int = u32; + type Float = f32; +} + #[derive(Copy, Clone, Debug)] pub struct Rv32GRegs; impl RegFile for Rv32GRegs { @@ -1767,6 +1823,36 @@ impl Instr16 { imm as i16 as i32 } + + #[inline(always)] + fn qk_byte_imm(self) -> i32 { + let b0 = self.extract_bits(12, 1); + let b21 = self.extract_bits(5, 2); + let b43 = self.extract_bits(10, 2); + + (b0 | (b21 << 1) | (b43 << 3)) as i32 + } + + #[inline(always)] + fn qk_half_imm(self) -> i32 { + let b21 = self.extract_bits(5, 2); + let b53 = self.extract_bits(10, 3); + + ((b21 << 1) | (b53 << 3)) as i32 + } + + #[inline(always)] + fn qk_byte_sp_imm(self) -> i32 { + self.extract_bits(7, 4) as i32 + } + + #[inline(always)] + fn qk_half_sp_imm(self) -> i32 { + let b31 = self.extract_bits(8, 3); + let b4 = self.extract_bits(7, 1); + + ((b31 << 1) | (b4 << 4)) as i32 + } } pub enum Instr { @@ -1807,7 +1893,14 @@ impl Instr { | Op::AddIW(ref i) | Op::SllIW(ref i) | Op::SrlIW(ref i) - | Op::SraIW(ref i) => { + | Op::SraIW(ref i) + | Op::BclrI(ref i) + | Op::BextI(ref i) + | Op::BinvI(ref i) + | Op::BsetI(ref i) + | Op::SllIUW(ref i) + | Op::RorI(ref i) + | Op::RorIW(ref i) => { ops.push(Operand::R(i.rd())); ops.push(Operand::R(i.rs1())); ops.push(Operand::I(i.imm())); @@ -1843,11 +1936,47 @@ impl Instr { | Op::DivW(ref r) | Op::DivUW(ref r) | Op::RemW(ref r) - | Op::RemUW(ref r) => { + | Op::RemUW(ref r) + | Op::Bclr(ref r) + | Op::Bext(ref r) + | Op::Binv(ref r) + | Op::Bset(ref r) + | Op::ShXAdd(_, ref r) + | Op::ShXAddUW(_, ref r) + | Op::AddUW(ref r) + | Op::Andn(ref r) + | Op::Orn(ref r) + | Op::Xnor(ref r) + | Op::Max(ref r) + | Op::MaxU(ref r) + | Op::Min(ref r) + | Op::MinU(ref r) + | Op::Rol(ref r) + | Op::Ror(ref r) + | Op::RolW(ref r) + | Op::RorW(ref r) + | Op::WchMcpy(ref r) => { ops.push(Operand::R(r.rd())); ops.push(Operand::R(r.rs1())); ops.push(Operand::R(r.rs2())); } + Op::SextB(ref i) + | Op::SextH(ref i) + | Op::Clz(ref i) + | Op::ClzW(ref i) + | Op::Ctz(ref i) + | Op::CtzW(ref i) + | Op::Cpop(ref i) + | Op::CpopW(ref i) + | Op::Orcb(ref i) + | Op::Rev8(ref i) => { + ops.push(Operand::R(i.rd())); + ops.push(Operand::R(i.rs1())); + } + Op::ZextH(ref r) => { + ops.push(Operand::R(r.rd())); + ops.push(Operand::R(r.rs1())); + } Op::Beq(ref b) | Op::Bne(ref b) | Op::Blt(ref b) @@ -2112,6 +2241,51 @@ impl<'a, D: RiscVDisassembler + 'a> Mnem<'a, D> { Op::Fcvt(..) | Op::FcvtToInt(..) | Op::FcvtFromInt(..) => "fcvt", Op::FmvToInt(..) | Op::FmvFromInt(..) => "fmv", Op::Fclass(..) => "fclass", + + Op::Bclr(..) => "bclr", + Op::Bext(..) => "bext", + Op::Binv(..) => "binv", + Op::Bset(..) => "bset", + Op::BclrI(..) => "bclri", + Op::BextI(..) => "bexti", + Op::BinvI(..) => "binvi", + Op::BsetI(..) => "bseti", + + Op::ShXAdd(x, _) => ["sh1add", "sh2add", "sh3add"][x as usize - 1], + Op::ShXAddUW(x, _) => ["sh1add.uw", "sh2add.uw", "sh3add.uw"][x as usize - 1], + Op::SllIUW(..) => "slli.uw", + Op::AddUW(..) => "add.uw", + + Op::Andn(..) => "andn", + Op::Orn(..) => "orn", + Op::Xnor(..) => "xnor", + + Op::Max(..) => "max", + Op::MaxU(..) => "maxu", + Op::Min(..) => "min", + Op::MinU(..) => "minu", + + Op::SextB(..) => "sext.b", + Op::SextH(..) => "sext.h", + Op::ZextH(..) => "zext.h", + + Op::Rol(..) => "rol", + Op::Ror(..) => "ror", + Op::RorI(..) => "rori", + Op::RolW(..) => "rolw", + Op::RorW(..) => "rorw", + Op::RorIW(..) => "roriw", + + Op::Clz(..) => "clz", + Op::ClzW(..) => "clzw", + Op::Ctz(..) => "ctz", + Op::CtzW(..) => "ctzw", + Op::Cpop(..) => "cpop", + Op::CpopW(..) => "cpopw", + Op::Orcb(..) => "orc.b", + Op::Rev8(..) => "rev8", + + Op::WchMcpy(..) => "qk.mcpy", }, } } @@ -2314,6 +2488,9 @@ impl fmt::Display for Mnem<'_, D> { pub trait StandardExtension { fn supported() -> bool; } +pub trait VendorExtension { + fn supported() -> bool; +} pub struct ExtensionNotImplemented; impl StandardExtension for ExtensionNotImplemented { @@ -2322,6 +2499,12 @@ impl StandardExtension for ExtensionNotImplemented { false } } +impl VendorExtension for ExtensionNotImplemented { + #[inline(always)] + fn supported() -> bool { + false + } +} pub struct ExtensionSupported; impl StandardExtension for ExtensionSupported { @@ -2330,12 +2513,22 @@ impl StandardExtension for ExtensionSupported { true } } +impl VendorExtension for ExtensionSupported { + #[inline(always)] + fn supported() -> bool { + true + } +} pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Sync { type RegFile: RegFile; type MulDivExtension: StandardExtension; type AtomicExtension: StandardExtension; type CompressedExtension: StandardExtension; + type BitmanipZbaExtension: StandardExtension; + type BitmanipZbbExtension: StandardExtension; + type BitmanipZbsExtension: StandardExtension; + type WCHExtension: VendorExtension; fn decode(addr: u64, bytes: &[u8]) -> DisResult> { use Error::*; @@ -2425,6 +2618,74 @@ pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Syn )) } + // WCH extensions + 0b001_00 if Self::WCHExtension::supported() => { + // qk.c.lbu + let rd = IntReg::new(8 + inst.extract_bits(2, 3) as u32); + let rs1 = IntReg::new(8 + inst.extract_bits(7, 3) as u32); + + let imm = inst.qk_byte_imm(); + Op::Load(LoadTypeInst::new(1, true, rd, rs1, imm)?) + } + 0b101_00 if Self::WCHExtension::supported() => { + // qk.c.sb + let rs2 = IntReg::new(8 + inst.extract_bits(2, 3) as u32); + let rs1 = IntReg::new(8 + inst.extract_bits(7, 3) as u32); + + let imm = inst.qk_byte_imm(); + Op::Store(StoreTypeInst::new(1, rs2, rs1, imm)?) + } + 0b001_10 if Self::WCHExtension::supported() => { + // qk.c.lhu + let rd = IntReg::new(8 + inst.extract_bits(2, 3) as u32); + let rs1 = IntReg::new(8 + inst.extract_bits(7, 3) as u32); + + let imm = inst.qk_half_imm(); + Op::Load(LoadTypeInst::new(2, true, rd, rs1, imm)?) + } + 0b101_10 if Self::WCHExtension::supported() => { + // qk.c.sh + let rs2 = IntReg::new(8 + inst.extract_bits(2, 3) as u32); + let rs1 = IntReg::new(8 + inst.extract_bits(7, 3) as u32); + + let imm = inst.qk_half_imm(); + Op::Store(StoreTypeInst::new(2, rs2, rs1, imm)?) + } + 0b100_00 if Self::WCHExtension::supported() && ((parcel >> 11) & 3 == 0) => { + let subop = (parcel >> 5) & 0b11; + match subop { + 0b00 => { + // qk.c.lbusp + let rd = IntReg::new(8 + inst.extract_bits(2, 3) as u32); + let imm = inst.qk_byte_sp_imm(); + + Op::Load(LoadTypeInst::new(1, true, rd, IntReg::new(2), imm)?) + } + 0b01 => { + // qk.c.lhusp + let rd = IntReg::new(8 + inst.extract_bits(2, 3) as u32); + let imm = inst.qk_half_sp_imm(); + + Op::Load(LoadTypeInst::new(2, true, rd, IntReg::new(2), imm)?) + } + 0b10 => { + // qk.c.sbsp + let rs2 = IntReg::new(8 + inst.extract_bits(2, 3) as u32); + let imm = inst.qk_byte_sp_imm(); + + Op::Store(StoreTypeInst::new(1, rs2, IntReg::new(2), imm)?) + } + 0b11 => { + // qk.c.shsp + let rs2 = IntReg::new(8 + inst.extract_bits(2, 3) as u32); + let imm = inst.qk_half_sp_imm(); + + Op::Store(StoreTypeInst::new(2, rs2, IntReg::new(2), imm)?) + } + _ => unreachable!(), + } + } + //0b001_00 if int_width == 16 => unimplemented!("LQ"), 0b001_00 if float_width >= 8 => { // FLD @@ -2778,12 +3039,27 @@ pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Syn // TODO CUSTOM_0 0b00011 => { // MISC-MEM - let itype = ITypeInst::new(inst)?; - - match inst.funct3() { - 0b000 => Op::Fence(itype), - 0b001 => Op::FenceI(itype), - _ => return Err(InvalidSubop), + if Self::WCHExtension::supported() + && inst.0 & 0b00000_11_00000_00000_11111111_1111111 + == 0b00000_00_00000_00000_11100000_0001111 + { + // qk.mcpy + let r_end = inst.extract_bits(15, 5); + let r_start = inst.extract_bits(20, 5); + let r_dst = inst.extract_bits(27, 5); + Op::WchMcpy(RTypeIntInst::from_ops( + IntReg::new(r_dst), + IntReg::new(r_start), + IntReg::new(r_end), + )) + } else { + let itype = ITypeInst::new(inst)?; + + match inst.funct3() { + 0b000 => Op::Fence(itype), + 0b001 => Op::FenceI(itype), + _ => return Err(InvalidSubop), + } } } 0b00100 => { @@ -2796,14 +3072,75 @@ pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Syn 0b100 => Op::XorI(itype), 0b110 => Op::OrI(itype), 0b111 => Op::AndI(itype), - 0b001 => Op::SllI(itype), // TODO shamt + 0b001 => { + let shift_opc = inst.0 >> 26; + let orig_imm = inst.i_imm(); + + // pretty terrible hack to clear bits for shamt + if int_width > 4 { + itype.inst.0 &= !0xfc000000; + } else { + itype.inst.0 &= !0xfe000000; + } + + match shift_opc { + 0b000000 => Op::SllI(itype), + 0b010010 if Self::BitmanipZbsExtension::supported() => { + Op::BclrI(itype) + } + 0b011010 if Self::BitmanipZbsExtension::supported() => { + Op::BinvI(itype) + } + 0b001010 if Self::BitmanipZbsExtension::supported() => { + Op::BsetI(itype) + } + 0b011000 if Self::BitmanipZbbExtension::supported() => { + match orig_imm & 0b11111 { + 0b00000 => Op::Clz(itype), + 0b00001 => Op::Ctz(itype), + 0b00010 => Op::Cpop(itype), + 0b00100 => Op::SextB(itype), + 0b00101 => Op::SextH(itype), + _ => return Err(InvalidSubop), + } + } + _ => return Err(InvalidSubop), + } + } 0b101 => { - if inst.0 & 0x40000000 == 0 { - Op::SrlI(itype) + let shift_opc = inst.0 >> 26; + let orig_imm = inst.i_imm(); + + // pretty terrible hack to clear bits for shamt + if int_width > 4 { + itype.inst.0 &= !0xfc000000; } else { - // pretty terrible hack, whatever - itype.inst.0 &= !0x40000000; - Op::SraI(itype) + itype.inst.0 &= !0xfe000000; + } + + match shift_opc { + 0b000000 => Op::SrlI(itype), + 0b001010 if Self::BitmanipZbbExtension::supported() => { + match orig_imm & 0b111111 { + 0b000111 => Op::Orcb(itype), + _ => return Err(InvalidSubop), + } + } + 0b010000 => Op::SraI(itype), + 0b010010 if Self::BitmanipZbsExtension::supported() => { + Op::BextI(itype) + } + 0b011000 if Self::BitmanipZbbExtension::supported() => { + Op::RorI(itype) + } + 0b011010 if Self::BitmanipZbbExtension::supported() => { + match orig_imm & 0b111111 { + 0b011000 if int_width == 4 => Op::Rev8(itype), + 0b111000 if int_width == 8 => Op::Rev8(itype), + _ => return Err(InvalidSubop), + } + } + _ => return Err(InvalidSubop), } } _ => unreachable!(), @@ -2817,14 +3154,38 @@ pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Syn let mut itype = ITypeInst::new(inst)?; match inst.funct3() { 0b000 => Op::AddIW(itype), - 0b001 => Op::SllIW(itype), // TODO shamt + 0b001 => { + let shift_opc = inst.0 >> 26; + let orig_imm = inst.i_imm(); + // pretty terrible hack to clear bits for shamt + itype.inst.0 &= !0xfc000000; + + match shift_opc { + 0b000000 => Op::SllIW(itype), + 0b000010 => Op::SllIUW(itype), + 0b011000 if Self::BitmanipZbbExtension::supported() => { + match orig_imm & 0b11111 { + 0b00000 => Op::ClzW(itype), + 0b00001 => Op::CtzW(itype), + 0b00010 => Op::CpopW(itype), + _ => return Err(InvalidSubop), + } + } + _ => return Err(InvalidSubop), + } + } 0b101 => { - if inst.0 & 0x40000000 == 0 { - Op::SrlIW(itype) - } else { - // pretty terrible hack, whatever - itype.inst.0 &= !0x40000000; - Op::SraIW(itype) + let shift_opc = inst.0 >> 26; + // pretty terrible hack to clear bits for shamt + itype.inst.0 &= !0xfc000000; + + match shift_opc { + 0b000000 => Op::SrlIW(itype), + 0b010000 => Op::SraIW(itype), + 0b011000 if Self::BitmanipZbbExtension::supported() => { + Op::RorIW(itype) + } + _ => return Err(InvalidSubop), } } _ => return Err(InvalidSubop), @@ -2883,7 +3244,10 @@ pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Syn }, 0b0100000 => match inst.funct3() { 0b000 => Op::Sub(rtype), + 0b100 if Self::BitmanipZbbExtension::supported() => Op::Xnor(rtype), 0b101 => Op::Sra(rtype), + 0b110 if Self::BitmanipZbbExtension::supported() => Op::Orn(rtype), + 0b111 if Self::BitmanipZbbExtension::supported() => Op::Andn(rtype), _ => return Err(InvalidSubop), }, 0b0000001 if Self::MulDivExtension::supported() => { @@ -2899,6 +3263,60 @@ pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Syn _ => unreachable!(), } } + 0b0000100 + if Self::BitmanipZbbExtension::supported() && int_width == 4 => + { + if inst.funct3() == 0b100 && inst.rs2() == 0 { + Op::ZextH(rtype) + } else { + return Err(InvalidSubop); + } + } + 0b0000101 if Self::BitmanipZbbExtension::supported() => { + match inst.funct3() { + 0b110 => Op::Max(rtype), + 0b111 => Op::MaxU(rtype), + 0b100 => Op::Min(rtype), + 0b101 => Op::MinU(rtype), + _ => return Err(InvalidSubop), + } + } + 0b0010000 if Self::BitmanipZbaExtension::supported() => { + match inst.funct3() { + 0b010 => Op::ShXAdd(1, rtype), + 0b100 => Op::ShXAdd(2, rtype), + 0b110 => Op::ShXAdd(3, rtype), + _ => return Err(InvalidSubop), + } + } + 0b0100100 if Self::BitmanipZbsExtension::supported() => { + match inst.funct3() { + 0b001 => Op::Bclr(rtype), + 0b101 => Op::Bext(rtype), + _ => return Err(InvalidSubop), + } + } + 0b0010100 if Self::BitmanipZbsExtension::supported() => { + if inst.funct3() == 0b001 { + Op::Bset(rtype) + } else { + return Err(InvalidSubop); + } + } + 0b0110000 if Self::BitmanipZbbExtension::supported() => { + match inst.funct3() { + 0b001 => Op::Rol(rtype), + 0b101 => Op::Ror(rtype), + _ => return Err(InvalidSubop), + } + } + 0b0110100 if Self::BitmanipZbsExtension::supported() => { + if inst.funct3() == 0b001 { + Op::Binv(rtype) + } else { + return Err(InvalidSubop); + } + } _ => return Err(InvalidSubop), } } @@ -2928,6 +3346,34 @@ pub trait RiscVDisassembler: 'static + Debug + Sized + Copy + Clone + Send + Syn _ => return Err(InvalidSubop), } } + 0b0000100 => match inst.funct3() { + 0b000 if Self::BitmanipZbaExtension::supported() => { + Op::AddUW(rtype) + } + 0b100 + if Self::BitmanipZbbExtension::supported() + && inst.rs2() == 0 + && int_width == 8 => + { + Op::ZextH(rtype) + } + _ => return Err(InvalidSubop), + }, + 0b0010000 if Self::BitmanipZbaExtension::supported() => { + match inst.funct3() { + 0b010 => Op::ShXAddUW(1, rtype), + 0b100 => Op::ShXAddUW(2, rtype), + 0b110 => Op::ShXAddUW(3, rtype), + _ => return Err(InvalidSubop), + } + } + 0b0110000 if Self::BitmanipZbbExtension::supported() => { + match inst.funct3() { + 0b001 => Op::RolW(rtype), + 0b101 => Op::RorW(rtype), + _ => return Err(InvalidSubop), + } + } _ => return Err(InvalidSubop), } } @@ -3181,4 +3627,22 @@ impl RiscVDisassembler for RiscVIMACDisassembler { type MulDivExtension = ExtensionSupported; type AtomicExtension = ExtensionSupported; type CompressedExtension = ExtensionSupported; + type BitmanipZbaExtension = ExtensionSupported; + type BitmanipZbbExtension = ExtensionSupported; + type BitmanipZbsExtension = ExtensionSupported; + type WCHExtension = ExtensionNotImplemented; +} + +#[derive(Copy, Clone, Debug)] +pub struct RiscVWCHDisassembler(); + +impl RiscVDisassembler for RiscVWCHDisassembler { + type RegFile = Rv32FRegs; + type MulDivExtension = ExtensionSupported; + type AtomicExtension = ExtensionSupported; + type CompressedExtension = ExtensionSupported; + type BitmanipZbaExtension = ExtensionSupported; + type BitmanipZbbExtension = ExtensionSupported; + type BitmanipZbsExtension = ExtensionSupported; + type WCHExtension = ExtensionSupported; } diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index 22ecdd6db9..d0d1211f3b 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -48,7 +48,7 @@ use binaryninja::low_level_il::{ }; use riscv_dis::{ FloatReg, FloatRegType, Instr, IntRegType, Op, RegFile, Register as RiscVRegister, - RiscVDisassembler, RoundMode, + RiscVDisassembler, RiscVWCHDisassembler, RoundMode, }; enum RegType { @@ -85,6 +85,12 @@ enum Intrinsic { FcvtUToF(u8, u8, RoundMode), FcvtFToU(u8, u8, RoundMode), Fence, + Clz, + Ctz, + Popcount, + OrCombine, + Rev8, + WchMcpy, } #[derive(Copy, Clone)] @@ -342,6 +348,12 @@ impl RiscVIntrinsic { 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()), + Some((26, _, _, _)) => Some(Intrinsic::Clz.into()), + Some((27, _, _, _)) => Some(Intrinsic::Ctz.into()), + Some((28, _, _, _)) => Some(Intrinsic::Popcount.into()), + Some((29, _, _, _)) => Some(Intrinsic::OrCombine.into()), + Some((30, _, _, _)) => Some(Intrinsic::Rev8.into()), + Some((31, _, _, _)) => Some(Intrinsic::WchMcpy.into()), _ => None, } } @@ -476,6 +488,12 @@ impl architecture::Intrinsic for RiscVIntrinsic { ) .into(), Intrinsic::Fence => "_fence".into(), + Intrinsic::Clz => "_clz".into(), + Intrinsic::Ctz => "_ctz".into(), + Intrinsic::Popcount => "_popcount".into(), + Intrinsic::OrCombine => "_orc_b".into(), + Intrinsic::Rev8 => "_rev8".into(), + Intrinsic::WchMcpy => "_wch_mcpy".into(), } } @@ -517,6 +535,12 @@ impl architecture::Intrinsic for RiscVIntrinsic { Self::id_from_parts(24, Some(usize), Some(fsize), Some(rm)) } Intrinsic::Fence => Self::id_from_parts(25, None, None, None), + Intrinsic::Clz => Self::id_from_parts(26, None, None, None), + Intrinsic::Ctz => Self::id_from_parts(27, None, None, None), + Intrinsic::Popcount => Self::id_from_parts(28, None, None, None), + Intrinsic::OrCombine => Self::id_from_parts(29, None, None, None), + Intrinsic::Rev8 => Self::id_from_parts(30, None, None, None), + Intrinsic::WchMcpy => Self::id_from_parts(31, None, None, None), } } @@ -585,6 +609,44 @@ impl architecture::Intrinsic for RiscVIntrinsic { Conf::new(Type::int(4, false), MIN_CONFIDENCE), )] } + Intrinsic::Clz + | Intrinsic::Ctz + | Intrinsic::Popcount + | Intrinsic::OrCombine + | Intrinsic::Rev8 => { + vec![NameAndType::new( + "input", + Conf::new( + Type::int(::Int::width(), false), + MIN_CONFIDENCE, + ), + )] + } + Intrinsic::WchMcpy => { + vec![ + NameAndType::new( + "dst", + Conf::new( + Type::int(::Int::width(), false), + MIN_CONFIDENCE, + ), + ), + NameAndType::new( + "start", + Conf::new( + Type::int(::Int::width(), false), + MIN_CONFIDENCE, + ), + ), + NameAndType::new( + "end", + Conf::new( + Type::int(::Int::width(), false), + MIN_CONFIDENCE, + ), + ), + ] + } } } @@ -595,7 +657,8 @@ impl architecture::Intrinsic for RiscVIntrinsic { | Intrinsic::Mret | Intrinsic::Wfi | Intrinsic::Csrwr - | Intrinsic::Fence => { + | Intrinsic::Fence + | Intrinsic::WchMcpy => { vec![] } Intrinsic::Csrrw | Intrinsic::Csrrd | Intrinsic::Csrrs | Intrinsic::Csrrc => { @@ -628,6 +691,16 @@ impl architecture::Intrinsic for RiscVIntrinsic { Intrinsic::FcvtFToU(_, size, _) => { vec![Conf::new(Type::int(size as usize, false), MAX_CONFIDENCE)] } + Intrinsic::Clz + | Intrinsic::Ctz + | Intrinsic::Popcount + | Intrinsic::OrCombine + | Intrinsic::Rev8 => { + vec![Conf::new( + Type::int(::Int::width(), false), + MIN_CONFIDENCE, + )] + } } } } @@ -799,6 +872,14 @@ impl Architecture for RiscVArch { operands.remove(2); } } + Op::AddUW(r) => { + // add.uw rd, rs, x0 => zext.w rd, rs + if r.rs2().id() == 0 { + mnem = "zext.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 { @@ -1151,6 +1232,80 @@ impl Architecture for RiscVArch { 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)), + Op::BclrI(i) => simple_i!(i, |rs1, shamt| { + let mask = il.not(max_width, il.lsl(max_width, 1, shamt)); + il.and(max_width, rs1, mask) + }), + Op::BextI(i) => simple_i!(i, |rs1, shamt| { + let val = il.lsr(max_width, rs1, shamt); + il.and(max_width, val, 1) + }), + Op::BinvI(i) => simple_i!(i, |rs1, shamt| { + let mask = il.lsl(max_width, 1, shamt); + il.xor(max_width, rs1, mask) + }), + Op::BsetI(i) => simple_i!(i, |rs1, shamt| { + let mask = il.lsl(max_width, 1, shamt); + il.or(max_width, rs1, mask) + }), + + Op::RorI(i) => simple_i!(i, |rs1, imm| il.ror(max_width, rs1, imm)), + Op::Clz(i) => { + let rd = Register::from(i.rd()); + let rs1 = LiftableLowLevelIL::lift(il, Register::from(i.rs1())); + + if i.rd().id() == 0 { + il.nop().append(); + } else { + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::Clz), [rs1]) + .append(); + } + } + Op::Ctz(i) => { + let rd = Register::from(i.rd()); + let rs1 = LiftableLowLevelIL::lift(il, Register::from(i.rs1())); + + if i.rd().id() == 0 { + il.nop().append(); + } else { + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::Ctz), [rs1]) + .append(); + } + } + Op::Cpop(i) => { + let rd = Register::from(i.rd()); + let rs1 = LiftableLowLevelIL::lift(il, Register::from(i.rs1())); + + if i.rd().id() == 0 { + il.nop().append(); + } else { + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::Popcount), [rs1]) + .append(); + } + } + Op::Orcb(i) => { + let rd = Register::from(i.rd()); + let rs1 = LiftableLowLevelIL::lift(il, Register::from(i.rs1())); + + if i.rd().id() == 0 { + il.nop().append(); + } else { + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::OrCombine), [rs1]) + .append(); + } + } + Op::Rev8(i) => { + let rd = Register::from(i.rd()); + let rs1 = LiftableLowLevelIL::lift(il, Register::from(i.rs1())); + + if i.rd().id() == 0 { + il.nop().append(); + } else { + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::Rev8), [rs1]) + .append(); + } + } + // 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)), @@ -1165,12 +1320,137 @@ impl Architecture for RiscVArch { 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)), + Op::Bclr(r) => simple_r!(r, |rs1, rs2| { + let shamt = il.and(max_width, rs2, (max_width * 8 - 1) as u64); + let mask = il.not(max_width, il.lsl(max_width, 1, shamt)); + il.and(max_width, rs1, mask) + }), + Op::Bext(r) => simple_r!(r, |rs1, rs2| { + let shamt = il.and(max_width, rs2, (max_width * 8 - 1) as u64); + let val = il.lsr(max_width, rs1, shamt); + il.and(max_width, val, 1) + }), + Op::Binv(r) => simple_r!(r, |rs1, rs2| { + let shamt = il.and(max_width, rs2, (max_width * 8 - 1) as u64); + let mask = il.lsl(max_width, 1, shamt); + il.xor(max_width, rs1, mask) + }), + Op::Bset(r) => simple_r!(r, |rs1, rs2| { + let shamt = il.and(max_width, rs2, (max_width * 8 - 1) as u64); + let mask = il.lsl(max_width, 1, shamt); + il.or(max_width, rs1, mask) + }), + + Op::ShXAdd(x, r) => simple_r!(r, |rs1, rs2| { + il.add(max_width, il.lsl(max_width, rs1, x), rs2) + }), + + Op::Andn(r) => simple_r!(r, |rs1, rs2| il.and(max_width, rs1, il.not(max_width, rs2))), + Op::Orn(r) => simple_r!(r, |rs1, rs2| il.or(max_width, rs1, il.not(max_width, rs2))), + Op::Xnor(r) => simple_r!(r, |rs1, rs2| il.not(max_width, il.xor(max_width, rs1, rs2))), + Op::SextB(i) => simple_i!(i, |rs1, _| { il.sx(max_width, il.low_part(1, rs1)) }), + Op::SextH(i) => simple_i!(i, |rs1, _| { il.sx(max_width, il.low_part(2, rs1)) }), + Op::ZextH(r) => simple_r!(r, |rs1, _| { il.zx(max_width, il.low_part(2, rs1)) }), + Op::Rol(r) => simple_r!(r, |rs1, rs2| { + let shamt = il.and(max_width, rs2, (max_width * 8 - 1) as u64); + il.rol(max_width, rs1, shamt) + }), + Op::Ror(r) => simple_r!(r, |rs1, rs2| { + let shamt = il.and(max_width, rs2, (max_width * 8 - 1) as u64); + il.ror(max_width, rs1, shamt) + }), + Op::Max(r) | Op::MaxU(r) | Op::Min(r) | Op::MinU(r) => { + let rd = Register::from(r.rd()); + if rd.id.0 == 0 { + il.nop().append() + } else { + let rs1 = Register::from(r.rs1()); + let rs2 = Register::from(r.rs2()); + + let cond = match op { + Op::Max(..) => il.cmp_sgt(max_width, rs1, rs2), + Op::MaxU(..) => il.cmp_ugt(max_width, rs1, rs2), + Op::Min(..) => il.cmp_slt(max_width, rs1, rs2), + Op::MinU(..) => il.cmp_ult(max_width, rs1, rs2), + _ => unreachable!(), + }; + + let mut t = LowLevelILLabel::new(); + let mut f = LowLevelILLabel::new(); + let mut end = LowLevelILLabel::new(); + + il.if_expr(cond, &mut t, &mut f).append(); + + il.mark_label(&mut t); + il.set_reg(max_width, rd, rs1).append(); + il.goto(&mut end).append(); + + il.mark_label(&mut f); + il.set_reg(max_width, rd, rs2).append(); + il.mark_label(&mut end); + } + } + + Op::WchMcpy(r) => { + let dst = LiftableLowLevelIL::lift(il, Register::from(r.rd())); + let start = LiftableLowLevelIL::lift(il, Register::from(r.rs1())); + let end = LiftableLowLevelIL::lift(il, Register::from(r.rs2())); + il.intrinsic::<_, LowLevelILRegisterKind>, _>( + [], + RiscVIntrinsic::::from(Intrinsic::WchMcpy), + [dst, start, end], + ) + .append(); + } + // 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))), + Op::SllIUW(i) => simple_i!(i, |rs1, imm| { + il.lsl(max_width, il.low_part(4, rs1), imm) + }), + + Op::RorIW(i) => simple_i!(i, |rs1, imm| il.sx(max_width, il.ror(4, rs1, imm))), + Op::ClzW(i) => { + let rd = Register::from(i.rd()); + let rs1 = + LiftableLowLevelILWithSize::lift_with_size(il, Register::from(i.rs1()), 4); + + if i.rd().id() == 0 { + il.nop().append(); + } else { + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::Clz), [rs1]) + .append(); + } + } + Op::CtzW(i) => { + let rd = Register::from(i.rd()); + let rs1 = + LiftableLowLevelILWithSize::lift_with_size(il, Register::from(i.rs1()), 4); + + if i.rd().id() == 0 { + il.nop().append(); + } else { + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::Ctz), [rs1]) + .append(); + } + } + Op::CpopW(i) => { + let rd = Register::from(i.rd()); + let rs1 = + LiftableLowLevelILWithSize::lift_with_size(il, Register::from(i.rs1()), 4); + + if i.rd().id() == 0 { + il.nop().append(); + } else { + il.intrinsic([rd], RiscVIntrinsic::::from(Intrinsic::Popcount), [rs1]) + .append(); + } + } + // 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))), @@ -1178,6 +1458,22 @@ impl Architecture for RiscVArch { 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::AddUW(r) => simple_r!(r, |rs1, rs2| { + il.add(max_width, il.low_part(4, rs1), rs2) + }), + Op::ShXAddUW(x, r) => { + simple_r!(r, |rs1, rs2| { il.add(max_width, il.lsl(4, rs1, x), rs2) }) + } + + Op::RolW(r) => simple_r!(r, |rs1, rs2| { + let shamt = il.and(max_width, rs2, 31); + il.sx(max_width, il.rol(4, rs1, shamt)) + }), + Op::RorW(r) => simple_r!(r, |rs1, rs2| { + let shamt = il.and(max_width, rs2, 31); + il.sx(max_width, il.ror(4, rs1, shamt)) + }), + Op::Mul(r) => simple_r!(r, |rs1, rs2| il.mul(max_width, rs1, rs2)), Op::MulH(r) => simple_r!(r, |rs1, rs2| { let extended_width = max_width * 2; @@ -2278,6 +2574,7 @@ impl RiscVELFRelocationHandler const R_RISCV_SUB64: u64 = 40; const R_RISCV_RVC_BRANCH: u64 = 44; const R_RISCV_RVC_JUMP: u64 = 45; + const R_RISCV_RELAX: u64 = 51; fn replace_b_imm(opcode: u32, imm: u32) -> u32 { (opcode & 0x01fff07f) @@ -2445,6 +2742,7 @@ impl RelocationHandler reloc.address ) } + Self::R_RISCV_RELAX => reloc.type_ = RelocationType::IgnoredRelocation, _ => { reloc.type_ = RelocationType::UnhandledRelocation; log::warn!( @@ -3025,6 +3323,14 @@ pub extern "C" fn CorePluginInit() -> bool { custom_handle, _dis: PhantomData, }); + let arch_wch = + architecture::register_architecture("rv32-wch", |custom_handle, core_arch| RiscVArch::< + RiscVWCHDisassembler, + > { + handle: core_arch, + custom_handle, + _dis: PhantomData, + }); let arch64 = architecture::register_architecture("rv64gc", |custom_handle, core_arch| RiscVArch::< RiscVIMACDisassembler, @@ -3041,6 +3347,13 @@ pub extern "C" fn CorePluginInit() -> bool { _dis: PhantomData, } }); + arch_wch.register_relocation_handler("ELF", |custom_handle, core_handler| { + RiscVELFRelocationHandler:: { + handle: core_handler, + custom_handle, + _dis: PhantomData, + } + }); arch64.register_relocation_handler("ELF", |custom_handle, core_handler| { RiscVELFRelocationHandler::> { handle: core_handler, @@ -3050,6 +3363,7 @@ pub extern "C" fn CorePluginInit() -> bool { }); arch32.register_function_recognizer(RiscVELFPLTRecognizer); + arch_wch.register_function_recognizer(RiscVELFPLTRecognizer); arch64.register_function_recognizer(RiscVELFPLTRecognizer); let cc32 = register_calling_convention( @@ -3058,6 +3372,9 @@ pub extern "C" fn CorePluginInit() -> bool { RiscVCC::>::new(), ); arch32.set_default_calling_convention(&cc32); + let cc32_wch = + register_calling_convention(arch_wch, "default", RiscVCC::::new()); + arch_wch.set_default_calling_convention(&cc32_wch); let cc64 = register_calling_convention( arch64, "default", diff --git a/view/elf/elfview.cpp b/view/elf/elfview.cpp index 8d0fde4676..83bf2fb902 100644 --- a/view/elf/elfview.cpp +++ b/view/elf/elfview.cpp @@ -1549,7 +1549,7 @@ bool ElfView::Init() else if (entry.section < m_elfSections.size()) { // symbol is relative to a section, look up by address instead of name to avoid ambiguity - uint64_t target = m_elfSections[entry.section].address + entry.value; + uint64_t target = imageBaseAdjustment + m_elfSections[entry.section].address + entry.value; auto symbol = GetSymbolByAddress(target); if (symbol) {