use capstone::arch::arm::ArmInsn;
use crate::safe_jit::arm_util::{bitflags, EncodingError};
pub const PC_OFFSET: usize = 4;
fn effective_pc(instr_pc: usize) -> usize {
(instr_pc + PC_OFFSET) & !3
}
fn flip16_if_be(raw: u32) -> u32 {
#[cfg(target_endian = "big")]
return (raw & 0xFFFF) << 16 | raw >> 16;
#[cfg(target_endian = "little")]
return raw;
}
bitflags! {
pub struct LdrImm: u32 {
rn set_rn try_set_rn: 0..4,
fixed0 set_fixed0: 4..7,
u set_u: 7..8, fixed1 set_fixed1: 8..16,
imm set_imm try_set_imm: 16..28,
rt set_rt try_set_rt: 28..32,
}
}
impl LdrImm {
#[allow(unused)]
pub fn new_lit(pc: usize, reg: u32, target: usize) -> Result<Self, EncodingError> {
let diff = target as isize - effective_pc(pc) as isize;
Self::new(reg, 0b1111, diff.try_into().map_err(|_| ())?)
}
pub fn new(rt: u32, rn: u32, imm: i32) -> Result<Self, EncodingError> {
if rn != 0b1111 && imm < 0 {
return Err(EncodingError);
}
let mut ins = Self::from_raw(0);
ins.try_set_imm(imm.unsigned_abs())?;
ins.try_set_rt(rt)?;
ins.try_set_rn(rn)?;
ins.set_fixed0(0b101);
ins.set_u(if imm >= 0 { 1 } else { 0 });
ins.set_fixed1(0b11111000);
Ok(ins)
}
pub fn bytes(self) -> [u8; 4] {
flip16_if_be(self.to_raw()).to_ne_bytes()
}
}
bitflags! {
struct LdrLitT1: u16 {
imm set_imm: 0..8,
rt set_rt: 8..11,
fixed: 11..16
}
struct LdrImmT1: u16 {
rt set_rt: 0..3,
rn set_rn: 3..6,
imm set_imm: 6..11,
fixed set_fixed: 11..16
}
struct LdrW: u32 {
rn set_rn: 0..4,
u set_u: 7..8,
imm set_imm: 16..28,
rt set_rt: 28..32
}
struct LdrShortImm: u32 {
rn set_rn: 0..4,
u set_u: 7..8,
imm set_imm: 16..24,
rt set_rt: 28..32
}
}
#[allow(private_interfaces)]
#[derive(Debug, Clone, Copy)]
pub enum LoadImm {
LdrLitT1(LdrLitT1),
LdrW(LdrW),
LdrT(LdrShortImm),
LdrD(LdrShortImm),
}
impl LoadImm {
pub fn try_from_raw(instr_id: ArmInsn, bytes: &[u8]) -> Option<Self> {
use ArmInsn::*;
if bytes.len() == 2 {
let raw = LdrLitT1::from_raw(u16::from_ne_bytes(bytes.try_into().unwrap()));
(raw.fixed() == 0b01001).then_some(Self::LdrLitT1(raw))
}
else {
let raw = flip16_if_be(u32::from_ne_bytes(bytes.try_into().ok()?));
match instr_id {
ARM_INS_LDR | ARM_INS_LDRB | ARM_INS_LDRH | ARM_INS_LDRSB | ARM_INS_LDRSH => {
Some(Self::LdrW(LdrW::from_raw(raw)))
}
ARM_INS_LDRT | ARM_INS_LDRBT | ARM_INS_LDRHT | ARM_INS_LDRSBT | ARM_INS_LDRSHT => {
Some(Self::LdrT(LdrShortImm::from_raw(raw)))
}
ARM_INS_LDRD => Some(Self::LdrD(LdrShortImm::from_raw(raw))),
_ => None,
}
}
}
pub fn rt(&self) -> u32 {
match self {
Self::LdrLitT1(ldr) => ldr.rt() as u32,
Self::LdrW(ldr) => ldr.rt(),
Self::LdrT(ldr) | Self::LdrD(ldr) => ldr.rt(),
}
}
pub fn target_pc(&self, instr_pc: usize) -> Option<usize> {
let pc = effective_pc(instr_pc);
match self {
Self::LdrLitT1(ldr) => Some(pc + ldr.imm() as usize * 4),
Self::LdrW(ldr) if ldr.rn() == 0b1111 => Some(pc + ldr.imm() as usize),
Self::LdrT(ldr) if ldr.rn() == 0b1111 => Some(pc + ldr.imm() as usize),
Self::LdrD(ldr) if ldr.rn() == 0b1111 => {
let imm = if ldr.u() == 1 { ldr.imm() as isize } else { -(ldr.imm() as isize) };
Some(pc.wrapping_sub_signed(imm * 4))
}
_ => None,
}
}
pub fn as_rt_load<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
match self {
Self::LdrLitT1(ldr) => {
let mut new = LdrImmT1::from_raw(0);
new.set_rt(ldr.rt());
new.set_rn(ldr.rt());
new.set_fixed(0b01101);
f(&new.to_raw().to_ne_bytes())
}
Self::LdrW(mut ldr) => {
ldr.set_rn(ldr.rt());
ldr.set_imm(0);
ldr.set_u(1);
f(&flip16_if_be(ldr.to_raw()).to_ne_bytes())
}
Self::LdrT(mut ldr) | Self::LdrD(mut ldr) => {
ldr.set_rn(ldr.rt());
ldr.set_imm(0);
if matches!(self, Self::LdrD(_)) {
ldr.set_u(1);
}
f(&flip16_if_be(ldr.to_raw()).to_ne_bytes())
}
}
}
}
bitflags! {
struct AdrT1: u16 {
imm8: 0..8,
rd: 8..11,
fixed: 11..16
}
struct AdrT23: u32 {
s1: 5..6,
s2: 7..8,
i1: 10..11,
imm8: 16..24,
rd: 24..28,
imm3: 28..31
}
}
#[allow(private_interfaces)]
#[derive(Debug)]
pub enum Adr {
T1(AdrT1),
T23(AdrT23),
}
impl Adr {
pub fn try_from_raw(bytes: &[u8]) -> Option<Self> {
if bytes.len() == 2 {
let ins = AdrT1::from_raw(u16::from_ne_bytes(bytes.try_into().unwrap()));
(ins.fixed() == 0b10100).then_some(Self::T1(ins))
}
else {
let raw = flip16_if_be(u32::from_ne_bytes(bytes.try_into().ok()?));
const MASK1: u32 = 0b0000_0000_0000_0000_1111_0010_0000_1111;
const MASK0: u32 = 0b0111_1111_1111_1111_1111_0110_1010_1111;
if raw & MASK1 != MASK1 || (raw | MASK0) != MASK0 {
return None;
}
let ins = AdrT23::from_raw(raw);
(ins.s1() != ins.s2()).then_some(Self::T23(ins))
}
}
pub fn dest_reg(&self) -> u32 {
match self {
Self::T1(t1) => t1.rd() as u32,
Self::T23(t23) => t23.rd(),
}
}
pub fn target_pc(&self, instr_pc: usize) -> usize {
let pc = effective_pc(instr_pc);
match self {
Self::T1(t1) => pc + t1.imm8() as usize * 4,
Self::T23(t23) => {
let full_imm = (t23.i1() << 11 | t23.imm3() << 8 | t23.imm8()) as usize;
if t23.s1() == 0 {
pc + full_imm
}
else {
pc - full_imm
}
}
}
}
}