#[derive(Debug, Clone, Copy)]
pub struct Instruction(pub u32);
impl Instruction {
#[inline]
pub fn new(value: u32) -> Self {
Self(value)
}
#[inline]
pub fn value(self) -> u32 {
self.0
}
}
#[inline]
pub fn is_adrp(instr: u32) -> bool {
(instr & 0x9F00_0000) == 0x9000_0000
}
#[inline]
pub fn is_add_imm(instr: u32) -> bool {
(instr & 0x7F80_0000) == 0x1100_0000
}
#[inline]
pub fn is_ldr_unsigned_imm(instr: u32) -> bool {
(instr & 0x3B40_0000) == 0x3940_0000
}
#[inline]
pub fn is_br(instr: u32) -> bool {
(instr & 0xFFFF_FC1F) == 0xD61F_0000
}
#[inline]
pub fn is_branch(instr: u32) -> bool {
(instr & 0x7C00_0000) == 0x1400_0000
}
#[inline]
pub fn is_bl(instr: u32) -> bool {
(instr & 0xFC00_0000) == 0x9400_0000
}
#[inline]
pub fn is_braa(instr: u32) -> bool {
(instr & 0xFFFF_FC00) == 0xD71F_0800
}
#[inline]
pub fn is_braaz(instr: u32) -> bool {
(instr & 0xFFFF_FC1F) == 0xD71F_081F
}
#[inline]
pub fn is_trap(instr: u32) -> bool {
(instr & 0xFFE0_001F) == 0xD420_0000
}
#[inline]
pub fn is_nop(instr: u32) -> bool {
instr == 0xD503_201F
}
#[inline]
pub fn is_ret(instr: u32) -> bool {
(instr & 0xFFFF_FC1F) == 0xD65F_0000
}
pub fn decode_adrp(instr: u32, pc: u64) -> u64 {
let immlo = ((instr >> 29) & 0x3) as u64;
let immhi = ((instr >> 5) & 0x7_FFFF) as u64;
let imm = (immhi << 2) | immlo;
let imm = if (imm & (1 << 20)) != 0 {
imm | 0xFFFF_FFFF_FFE0_0000
} else {
imm
};
let offset = imm << 12;
(pc & !0xFFF).wrapping_add(offset as u64)
}
pub fn decode_add_imm(instr: u32) -> u32 {
let imm12 = (instr >> 10) & 0xFFF;
let shift = (instr >> 22) & 0x3;
if shift == 1 { imm12 << 12 } else { imm12 }
}
pub fn decode_ldr_offset(instr: u32) -> u32 {
let imm12 = (instr >> 10) & 0xFFF;
let size = (instr >> 30) & 0x3;
imm12 << size
}
pub fn decode_branch(instr: u32, pc: u64) -> u64 {
let imm26 = (instr & 0x03FF_FFFF) as i64;
let imm26 = if (imm26 & (1 << 25)) != 0 {
imm26 | !0x03FF_FFFF
} else {
imm26
};
let offset = imm26 << 2;
pc.wrapping_add(offset as u64)
}
pub fn adrp_rd(instr: u32) -> u8 {
(instr & 0x1F) as u8
}
pub fn add_rd(instr: u32) -> u8 {
(instr & 0x1F) as u8
}
pub fn br_rn(instr: u32) -> u8 {
((instr >> 5) & 0x1F) as u8
}
pub fn encode_adrp(rd: u8, pc: u64, target: u64) -> u32 {
let target_page = target & !0xFFF;
let pc_page = pc & !0xFFF;
let delta = target_page.wrapping_sub(pc_page) as i64;
let imm = (delta >> 12) as u32;
let immlo = (imm & 0x3) << 29;
let immhi = ((imm >> 2) & 0x7_FFFF) << 5;
0x9000_0000 | immlo | immhi | (rd as u32)
}
pub fn encode_add_imm(rd: u8, rn: u8, imm: u32) -> u32 {
let imm12 = (imm & 0xFFF) << 10;
let sf = 1u32 << 31;
0x1100_0000 | sf | imm12 | ((rn as u32) << 5) | (rd as u32)
}
pub fn encode_ldr_unsigned(rt: u8, rn: u8, offset: u64) -> u32 {
let imm12 = ((offset >> 3) & 0xFFF) as u32; let size = 3u32;
0x3940_0000 | (size << 30) | (1 << 22) | (imm12 << 10) | ((rn as u32) << 5) | (rt as u32)
}
pub fn encode_br(rn: u8) -> u32 {
0xD61F_0000 | ((rn as u32) << 5)
}
pub fn encode_b(pc: u64, target: u64) -> u32 {
let offset = target.wrapping_sub(pc) as i64;
let imm26 = ((offset >> 2) & 0x03FF_FFFF) as u32;
0x1400_0000 | imm26
}
pub fn encode_bl(pc: u64, target: u64) -> u32 {
let offset = target.wrapping_sub(pc) as i64;
let imm26 = ((offset >> 2) & 0x03FF_FFFF) as u32;
0x9400_0000 | imm26
}
pub fn encode_braa(rn: u8, rm: u8) -> u32 {
0xD71F_0800 | ((rn as u32) << 5) | (rm as u32)
}
pub fn encode_braaz(rn: u8) -> u32 {
0xD71F_081F | ((rn as u32) << 5)
}
pub fn encode_nop() -> u32 {
0xD503_201F
}
pub fn encode_brk(imm: u16) -> u32 {
0xD420_0000 | ((imm as u32) << 5)
}
pub fn follow_adrp_add(adrp_addr: u64, instr0: u32, instr1: u32) -> Option<u64> {
if !is_adrp(instr0) {
return None;
}
let page = decode_adrp(instr0, adrp_addr);
if is_add_imm(instr1) {
let offset = decode_add_imm(instr1);
Some(page + offset as u64)
} else if is_ldr_unsigned_imm(instr1) {
let offset = decode_ldr_offset(instr1);
Some(page + offset as u64)
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_adrp() {
assert!(is_adrp(0x90000010)); assert!(!is_adrp(0xD61F0200)); }
#[test]
fn test_decode_branch() {
let pc = 0x1000;
let target = decode_branch(0x94000004, pc); assert_eq!(target, 0x1010);
}
#[test]
fn test_encode_decode_adrp() {
let pc = 0x1_8000_0000u64;
let target = 0x1_8000_1000u64;
let instr = encode_adrp(16, pc, target);
assert!(is_adrp(instr));
let decoded = decode_adrp(instr, pc);
assert_eq!(decoded, target & !0xFFF);
}
#[test]
fn test_is_braa() {
let instr = encode_braa(16, 17);
assert!(is_braa(instr));
assert!(!is_braa(encode_br(16)));
}
#[test]
fn test_encode_b() {
let pc = 0x1000u64;
let target = 0x1100u64;
let instr = encode_b(pc, target);
assert!(is_branch(instr));
let decoded = decode_branch(instr, pc);
assert_eq!(decoded, target);
}
}