use super::{BcjArch, NoState};
#[inline]
fn rd_le32(b: &[u8]) -> u32 {
u32::from_le_bytes([b[0], b[1], b[2], b[3]])
}
#[inline]
fn wr_le32(b: &mut [u8], v: u32) {
b[..4].copy_from_slice(&v.to_le_bytes());
}
#[inline]
fn rd_be32(b: &[u8]) -> u32 {
u32::from_be_bytes([b[0], b[1], b[2], b[3]])
}
#[inline]
fn wr_be32(b: &mut [u8], v: u32) {
b[..4].copy_from_slice(&v.to_be_bytes());
}
#[derive(Debug, Clone, Copy, Default)]
pub struct X86;
#[derive(Debug, Clone, Copy, Default)]
pub struct X86State {
prev_mask: u32,
prev_pos: u32,
seen: bool,
}
#[inline]
fn x86_test86(b: u8) -> bool {
b == 0x00 || b == 0xFF
}
const X86_MASK_TO_ALLOWED: [bool; 8] = [true, true, true, false, true, false, false, false];
const X86_MASK_TO_BIT_NUMBER: [u32; 8] = [0, 1, 2, 2, 3, 3, 3, 3];
impl X86 {
fn convert_inner(data: &mut [u8], ip: u32, encode: bool, st: &mut X86State) -> usize {
let len = data.len();
if len < 5 {
return 0;
}
let mut prev_mask = st.prev_mask;
if st.seen {
let d = ip.wrapping_sub(st.prev_pos);
if d > 3 {
prev_mask = 0;
} else if d > 0 {
prev_mask = (prev_mask << d) & 0x7;
}
}
let mut buffer_pos = 0usize;
let mut done = 0usize;
while buffer_pos + 4 < len {
if data[buffer_pos] & 0xFE != 0xE8 {
buffer_pos += 1;
done = buffer_pos;
continue;
}
if prev_mask != 0 {
let index = X86_MASK_TO_BIT_NUMBER[(prev_mask & 0x7) as usize] as usize;
let b = data[buffer_pos + 4 - index];
if !X86_MASK_TO_ALLOWED[(prev_mask & 0x7) as usize] || x86_test86(b) {
prev_mask = ((prev_mask << 1) & 0x7) | 1;
buffer_pos += 1;
done = buffer_pos;
continue;
}
}
if x86_test86(data[buffer_pos + 4]) {
let src = rd_le32(&data[buffer_pos + 1..]);
let pos = ip.wrapping_add(buffer_pos as u32).wrapping_add(5);
let mut cur = src;
let dest;
loop {
let d = if encode {
cur.wrapping_add(pos)
} else {
cur.wrapping_sub(pos)
};
if prev_mask == 0 {
dest = d;
break;
}
let idx = X86_MASK_TO_BIT_NUMBER[(prev_mask & 0x7) as usize] * 8;
let b = (d >> (24 - idx)) as u8;
if !x86_test86(b) {
dest = d;
break;
}
cur = d ^ ((1u32 << (32 - idx)).wrapping_sub(1));
}
let top = if (dest >> 24) & 1 != 0 { 0xFFu32 } else { 0 };
let outv = (dest & 0x00FF_FFFF) | (top << 24);
wr_le32(&mut data[buffer_pos + 1..], outv);
buffer_pos += 5;
done = buffer_pos;
prev_mask = 0;
} else {
prev_mask = ((prev_mask << 1) & 0x7) | 1;
buffer_pos += 1;
done = buffer_pos;
}
}
st.prev_mask = prev_mask;
st.prev_pos = ip.wrapping_add(done as u32);
st.seen = true;
done
}
}
impl BcjArch for X86 {
const NAME: &'static str = "bcj-x86";
const EXT: &'static str = "bcj-x86";
const ALIGN: usize = 1;
type State = X86State;
fn convert(data: &mut [u8], ip: u32, encode: bool, state: &mut X86State) -> usize {
X86::convert_inner(data, ip, encode, state)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Arm;
impl BcjArch for Arm {
const NAME: &'static str = "bcj-arm";
const EXT: &'static str = "bcj-arm";
const ALIGN: usize = 4;
type State = NoState;
fn convert(data: &mut [u8], ip: u32, encode: bool, _: &mut NoState) -> usize {
let mut i = 0usize;
while i + 4 <= data.len() {
if data[i + 3] == 0xEB {
let src = (data[i + 2] as u32) << 16 | (data[i + 1] as u32) << 8 | (data[i] as u32);
let src = src << 2;
let pos = ip.wrapping_add(i as u32).wrapping_add(8);
let dest = if encode {
src.wrapping_add(pos)
} else {
src.wrapping_sub(pos)
};
let dest = dest >> 2;
data[i + 2] = (dest >> 16) as u8;
data[i + 1] = (dest >> 8) as u8;
data[i] = dest as u8;
}
i += 4;
}
i
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ArmThumb;
impl BcjArch for ArmThumb {
const NAME: &'static str = "bcj-armt";
const EXT: &'static str = "bcj-armt";
const ALIGN: usize = 2;
type State = NoState;
fn convert(data: &mut [u8], ip: u32, encode: bool, _: &mut NoState) -> usize {
let mut i = 0usize;
while i + 4 <= data.len() {
if (data[i + 1] & 0xF8) == 0xF0 && (data[i + 3] & 0xF8) == 0xF8 {
let src = ((data[i + 1] as u32 & 0x07) << 19)
| ((data[i] as u32) << 11)
| ((data[i + 3] as u32 & 0x07) << 8)
| (data[i + 2] as u32);
let src = src << 1;
let pos = ip.wrapping_add(i as u32).wrapping_add(4);
let dest = if encode {
src.wrapping_add(pos)
} else {
src.wrapping_sub(pos)
};
let dest = dest >> 1;
data[i + 1] = 0xF0 | ((dest >> 19) as u8 & 0x07);
data[i] = (dest >> 11) as u8;
data[i + 3] = 0xF8 | ((dest >> 8) as u8 & 0x07);
data[i + 2] = dest as u8;
i += 2;
}
i += 2;
}
i
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Arm64;
impl BcjArch for Arm64 {
const NAME: &'static str = "bcj-arm64";
const EXT: &'static str = "bcj-arm64";
const ALIGN: usize = 4;
type State = NoState;
fn convert(data: &mut [u8], ip: u32, encode: bool, _: &mut NoState) -> usize {
let mut i = 0usize;
while i + 4 <= data.len() {
let instr = rd_le32(&data[i..]);
let pc = ip.wrapping_add(i as u32);
if (instr >> 26) == 0x25 {
let src = instr & 0x03FF_FFFF;
let addr = if encode {
src.wrapping_add(pc >> 2)
} else {
src.wrapping_sub(pc >> 2)
} & 0x03FF_FFFF;
let out = (0x25 << 26) | addr;
wr_le32(&mut data[i..], out);
} else if (instr & 0x9F00_0000) == 0x9000_0000 {
let immlo = (instr >> 29) & 0x3;
let immhi = (instr >> 5) & 0x0007_FFFF;
let src = (immhi << 2) | immlo; let src21 = src & 0x001F_FFFF;
let pagepc = pc >> 12;
let addr = if encode {
src21.wrapping_add(pagepc)
} else {
src21.wrapping_sub(pagepc)
} & 0x001F_FFFF;
let new_immlo = addr & 0x3;
let new_immhi = (addr >> 2) & 0x0007_FFFF;
let out = (instr & 0x9F00_001F) | (new_immlo << 29) | (new_immhi << 5);
wr_le32(&mut data[i..], out);
}
i += 4;
}
i
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Ppc;
impl BcjArch for Ppc {
const NAME: &'static str = "bcj-ppc";
const EXT: &'static str = "bcj-ppc";
const ALIGN: usize = 4;
type State = NoState;
fn convert(data: &mut [u8], ip: u32, encode: bool, _: &mut NoState) -> usize {
let mut i = 0usize;
while i + 4 <= data.len() {
if (data[i] & 0xFC) == 0x48 && (data[i + 3] & 0x03) == 0x01 {
let instr = rd_be32(&data[i..]);
let src = instr & 0x03FF_FFFC; let pos = ip.wrapping_add(i as u32);
let dest = if encode {
src.wrapping_add(pos)
} else {
src.wrapping_sub(pos)
};
let out = 0x4800_0000 | (dest & 0x03FF_FFFC) | (instr & 0x3);
wr_be32(&mut data[i..], out);
}
i += 4;
}
i
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Sparc;
impl BcjArch for Sparc {
const NAME: &'static str = "bcj-sparc";
const EXT: &'static str = "bcj-sparc";
const ALIGN: usize = 4;
type State = NoState;
fn convert(data: &mut [u8], ip: u32, encode: bool, _: &mut NoState) -> usize {
let mut i = 0usize;
while i + 4 <= data.len() {
if (data[i] == 0x40 && (data[i + 1] & 0xC0) == 0x00)
|| (data[i] == 0x7F && (data[i + 1] & 0xC0) == 0xC0)
{
let instr = rd_be32(&data[i..]);
let src = (instr & 0x3FFF_FFFF) << 2;
let pos = ip.wrapping_add(i as u32);
let dest = if encode {
src.wrapping_add(pos)
} else {
src.wrapping_sub(pos)
};
let dest = dest >> 2;
let dest = (0x4000_0000u32.wrapping_sub(dest & 0x0040_0000))
| 0x4000_0000
| (dest & 0x003F_FFFF);
wr_be32(&mut data[i..], dest);
}
i += 4;
}
i
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Ia64;
const IA64_BRANCH_TABLE: [u8; 32] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, 7, 7, 4, 4, 0, 0, 4, 4, 0, 0,
];
impl BcjArch for Ia64 {
const NAME: &'static str = "bcj-ia64";
const EXT: &'static str = "bcj-ia64";
const ALIGN: usize = 16;
type State = NoState;
fn convert(data: &mut [u8], ip: u32, encode: bool, _: &mut NoState) -> usize {
let mut i = 0usize;
while i + 16 <= data.len() {
let template = (data[i] & 0x1F) as usize;
let mask = IA64_BRANCH_TABLE[template];
for slot in 0..3u32 {
if (mask >> slot) & 1 == 0 {
continue;
}
let bit_pos = 5 + slot * 41;
let byte_pos = (bit_pos >> 3) as usize;
let bit_res = bit_pos & 7;
let mut instr: u64 = 0;
for j in 0..6 {
instr |= (data[i + byte_pos + j] as u64) << (8 * j);
}
let inst_norm = instr >> bit_res;
if ((inst_norm >> 37) & 0xF) == 5 && ((inst_norm >> 9) & 0x7) == 0 {
let mut src = ((inst_norm >> 13) & 0x0F_FFFF) as u32;
src |= (((inst_norm >> 36) & 1) as u32) << 20;
let src = src << 4;
let pos = ip.wrapping_add(i as u32);
let dest = if encode {
src.wrapping_add(pos)
} else {
src.wrapping_sub(pos)
};
let dest = dest >> 4;
let mut inst_norm = inst_norm;
inst_norm &= !(0x0F_FFFFu64 << 13);
inst_norm |= ((dest & 0x0F_FFFF) as u64) << 13;
inst_norm &= !(1u64 << 36);
inst_norm |= (((dest >> 20) & 1) as u64) << 36;
let mut instr2 = instr;
let keep_mask = (1u64 << bit_res) - 1;
instr2 &= keep_mask;
instr2 |= inst_norm << bit_res;
for j in 0..6 {
data[i + byte_pos + j] = (instr2 >> (8 * j)) as u8;
}
}
}
i += 16;
}
i
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct RiscV;
impl BcjArch for RiscV {
const NAME: &'static str = "bcj-riscv";
const EXT: &'static str = "bcj-riscv";
const ALIGN: usize = 4;
type State = NoState;
fn convert(data: &mut [u8], ip: u32, encode: bool, _: &mut NoState) -> usize {
let mut i = 0usize;
while i + 4 <= data.len() {
let instr = rd_le32(&data[i..]);
if (instr & 0x7F) == 0x6F {
let rd = (instr >> 7) & 0x1F;
if rd == 1 {
let imm20 = (instr >> 31) & 0x1;
let imm10_1 = (instr >> 21) & 0x3FF;
let imm11 = (instr >> 20) & 0x1;
let imm19_12 = (instr >> 12) & 0xFF;
let off = (imm20 << 20) | (imm19_12 << 12) | (imm11 << 11) | (imm10_1 << 1);
let pos = ip.wrapping_add(i as u32);
let dest = if encode {
off.wrapping_add(pos)
} else {
off.wrapping_sub(pos)
} & 0x001F_FFFF; let n_imm20 = (dest >> 20) & 0x1;
let n_imm10_1 = (dest >> 1) & 0x3FF;
let n_imm11 = (dest >> 11) & 0x1;
let n_imm19_12 = (dest >> 12) & 0xFF;
let out = (instr & 0x0000_0FFF) | (n_imm19_12 << 12)
| (n_imm11 << 20)
| (n_imm10_1 << 21)
| (n_imm20 << 31);
wr_le32(&mut data[i..], out);
}
}
i += 4;
}
i
}
}