add instruction using pseudo-instruction format

This commit is contained in:
Jean-Marie Mineau 2023-12-15 14:55:55 +01:00
parent 95f4686f3f
commit 2d164362a7
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
2 changed files with 209 additions and 8 deletions

View file

@ -8,6 +8,8 @@ use crate::{DexString, IdField, IdMethod, IdMethodType, IdType, MethodHandle, Re
use anyhow::anyhow; use anyhow::anyhow;
use pyo3::prelude::*; use pyo3::prelude::*;
use std::collections::HashMap;
#[pyclass] #[pyclass]
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct CallSite; // TODO pub struct CallSite; // TODO
@ -743,7 +745,83 @@ impl FilledNewArray {
} }
} }
// TODO: fill-array-data #[pyclass]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FillArrayData {
pub arr: u8,
pub elt_width: u16,
pub data: Vec<u8>,
}
#[pymethods]
impl FillArrayData {
#[new]
pub fn new(arr: u8, elt_width: u16, data: Vec<u8>) -> Self {
Self {
arr,
elt_width,
data,
}
}
pub fn __str__(&self) -> String {
let data: String = if self.data.len() / self.elt_width as usize == 0 {
"".into()
} else if self.data.len() / self.elt_width as usize <= 2 {
let mut arr = "".into();
for (i, v) in self.data.iter().enumerate() {
if i == 0 {
arr += "0x"
} else if i % self.elt_width as usize == 0 {
arr += " 0x"
}
arr += format!("{v:02x}").as_str();
}
arr
} else {
let mut arr = "0x".into();
for v in &self.data[..self.elt_width as usize] {
arr += format!("{v:02x}").as_str();
}
arr += " ... 0x";
for v in &self.data[self.data.len() - self.elt_width as usize..] {
arr += format!("{v:02x}").as_str();
}
arr
};
format!("fill-array-data {} {}", self.arr, data)
}
pub fn __repr__(&self) -> String {
let data: String = if self.data.len() / self.elt_width as usize == 0 {
"".into()
} else if self.data.len() / self.elt_width as usize <= 2 {
let mut arr = "".into();
for (i, v) in self.data.iter().enumerate() {
if i == 0 {
arr += "0x"
} else if i % self.elt_width as usize == 0 {
arr += ", 0x"
}
arr += format!("{v:02x}").as_str();
}
arr
} else {
let mut arr = "0x".into();
for v in &self.data[..self.elt_width as usize] {
arr += format!("{v:02x}").as_str();
}
arr += ", ..., 0x";
for v in &self.data[self.data.len() - self.elt_width as usize..] {
arr += format!("{v:02x}").as_str();
}
arr
};
format!("Instruction(FillArrayData({}, [{}]))", self.arr, data)
}
}
/// Throws the exception in the register. /// Throws the exception in the register.
#[pyclass] #[pyclass]
@ -791,8 +869,36 @@ impl Goto {
} }
} }
// TODO packed-switch /// Jump to a label depending on the value of a register. If the value
/// is not matched, continue the extecution at the next instruction.
#[pyclass]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Switch {
pub reg: u8,
pub branches: HashMap<i32, String>,
}
#[pymethods]
impl Switch {
#[new]
pub fn new(reg: u8, branches: HashMap<i32, String>) -> Self {
Self { reg, branches }
}
pub fn __str__(&self) -> String {
let mut branches_str: String = "".into();
let mut branches: Vec<_> = self.branches.iter().collect();
branches.sort_by_key(|(key, _)| key);
for (key, label) in branches {
branches_str += &format!("\n {key}: goto {label}");
}
format!("switch {} {}", self.reg, branches_str)
}
pub fn __repr__(&self) -> String {
format!("Instruction(Switch({}, ...))", self.reg)
}
}
/// Store the result of the comparison between the registers. /// Store the result of the comparison between the registers.
/// ///
/// - b < c: a = -1 /// - b < c: a = -1

View file

@ -4,6 +4,7 @@
use crate::{Error, ReadSeek, Result, Serializable}; use crate::{Error, ReadSeek, Result, Serializable};
//use log::debug; //use log::debug;
use std::io::{SeekFrom, Write}; use std::io::{SeekFrom, Write};
use std::iter::zip;
/// An instruction, following the formats described at /// An instruction, following the formats described at
/// <https://source.android.com/docs/core/runtime/dalvik-bytecode> /// <https://source.android.com/docs/core/runtime/dalvik-bytecode>
@ -208,6 +209,17 @@ pub enum Instruction {
op: u8, op: u8,
b: i64, b: i64,
}, },
FormatPackedSwitchPayload {
first_key: i32,
targets: Vec<i32>,
},
FormatSparseSwitchPayload {
key_targets: Vec<(i32, i32)>,
},
FormatFillArrayDataPayload {
elt_width: u16,
data: Vec<u8>,
},
} }
impl Instruction { impl Instruction {
@ -595,6 +607,48 @@ impl Instruction {
let b = i64::deserialize(input)?; let b = i64::deserialize(input)?;
Ok(Self::Format51L { va, op, b }) Ok(Self::Format51L { va, op, b })
} }
pub fn deserialize_packed_switch(input: &mut dyn ReadSeek) -> Result<Self> {
let _ = u16::deserialize(input)?;
let size = u16::deserialize(input)?;
let first_key = i32::deserialize(input)?;
let mut targets = vec![];
for _ in 0..size {
targets.push(i32::deserialize(input)?);
}
Ok(Self::FormatPackedSwitchPayload { first_key, targets })
}
pub fn deserialize_sparse_switch(input: &mut dyn ReadSeek) -> Result<Self> {
let _ = u16::deserialize(input)?;
let size = u16::deserialize(input)?;
let mut keys = vec![];
let mut targets = vec![];
for _ in 0..size {
keys.push(i32::deserialize(input)?);
}
for _ in 0..size {
targets.push(i32::deserialize(input)?);
}
let key_targets = zip(keys, targets).collect();
Ok(Self::FormatSparseSwitchPayload { key_targets })
}
pub fn deserialize_fill_array_data(input: &mut dyn ReadSeek) -> Result<Self> {
let _ = u16::deserialize(input)?;
let elt_width = u16::deserialize(input)?;
let size = u32::deserialize(input)?;
let len = size * elt_width as u32;
let mut data = vec![];
for _ in 0..len {
data.push(u8::deserialize(input)?);
}
if len % 2 != 0 {
let _ = u8::deserialize(input)?;
}
Ok(Self::FormatFillArrayDataPayload { elt_width, data })
}
} }
impl Serializable for Instruction { impl Serializable for Instruction {
@ -771,7 +825,6 @@ impl Serializable for Instruction {
op.serialize(output)?; op.serialize(output)?;
0u8.serialize(output)?; 0u8.serialize(output)?;
let [a_h0, a_h1, a_l0, a_l1] = a.to_be_bytes(); let [a_h0, a_h1, a_l0, a_l1] = a.to_be_bytes();
// TODO: check the bytes order
u32::from_be_bytes([a_l0, a_l1, a_h0, a_h1]).serialize(output) u32::from_be_bytes([a_l0, a_l1, a_h0, a_h1]).serialize(output)
} }
Self::Format32X { op, va, vb } => { Self::Format32X { op, va, vb } => {
@ -784,21 +837,18 @@ impl Serializable for Instruction {
op.serialize(output)?; op.serialize(output)?;
va.serialize(output)?; va.serialize(output)?;
let [b_h0, b_h1, b_l0, b_l1] = b.to_be_bytes(); let [b_h0, b_h1, b_l0, b_l1] = b.to_be_bytes();
// TODO: check the bytes order
u32::from_be_bytes([b_l0, b_l1, b_h0, b_h1]).serialize(output) u32::from_be_bytes([b_l0, b_l1, b_h0, b_h1]).serialize(output)
} }
Self::Format31T { va, op, b } => { Self::Format31T { va, op, b } => {
op.serialize(output)?; op.serialize(output)?;
va.serialize(output)?; va.serialize(output)?;
let [b_h0, b_h1, b_l0, b_l1] = b.to_be_bytes(); let [b_h0, b_h1, b_l0, b_l1] = b.to_be_bytes();
// TODO: check the bytes order
u32::from_be_bytes([b_l0, b_l1, b_h0, b_h1]).serialize(output) u32::from_be_bytes([b_l0, b_l1, b_h0, b_h1]).serialize(output)
} }
Self::Format31C { va, op, b } => { Self::Format31C { va, op, b } => {
op.serialize(output)?; op.serialize(output)?;
va.serialize(output)?; va.serialize(output)?;
let [b_h0, b_h1, b_l0, b_l1] = b.to_be_bytes(); let [b_h0, b_h1, b_l0, b_l1] = b.to_be_bytes();
// TODO: check the bytes order
u32::from_be_bytes([b_l0, b_l1, b_h0, b_h1]).serialize(output) u32::from_be_bytes([b_l0, b_l1, b_h0, b_h1]).serialize(output)
} }
Self::Format35C { Self::Format35C {
@ -1053,6 +1103,41 @@ impl Serializable for Instruction {
va.serialize(output)?; va.serialize(output)?;
b.serialize(output) b.serialize(output)
} }
Self::FormatPackedSwitchPayload { first_key, targets } => {
0x0100u16.serialize(output)?;
let size = targets.len() as u16;
size.serialize(output)?;
first_key.serialize(output)?;
for target in targets {
target.serialize(output)?;
}
Ok(())
}
Self::FormatSparseSwitchPayload { key_targets } => {
0x0200u16.serialize(output)?;
let size = key_targets.len() as u16;
size.serialize(output)?;
for (key, _) in key_targets {
key.serialize(output)?;
}
for (_, target) in key_targets {
target.serialize(output)?;
}
Ok(())
}
Self::FormatFillArrayDataPayload { elt_width, data } => {
0x0300u16.serialize(output)?;
elt_width.serialize(output)?;
let size = (data.len() / *elt_width as usize) as u32;
size.serialize(output)?;
for d in data {
d.serialize(output)?;
}
if data.len() % 2 != 0 {
0u8.serialize(output)?;
}
Ok(())
}
} }
} }
@ -1061,13 +1146,18 @@ impl Serializable for Instruction {
Error::SerializationError(format!("Failled to get position in steam: {err}")) Error::SerializationError(format!("Failled to get position in steam: {err}"))
})?; })?;
let op = u8::deserialize(input)?; let op = u8::deserialize(input)?;
let _ = u8::deserialize(input)?; let id = u8::deserialize(input)?;
input.seek(SeekFrom::Start(pos)).map_err(|err| { input.seek(SeekFrom::Start(pos)).map_err(|err| {
Error::SerializationError(format!("Failled to get to position in steam: {err}")) Error::SerializationError(format!("Failled to get to position in steam: {err}"))
})?; })?;
match op { match op {
0x00 => Self::deserialize_10x(input), 0x00 => match id {
0x01 => Self::deserialize_packed_switch(input),
0x02 => Self::deserialize_sparse_switch(input),
0x03 => Self::deserialize_fill_array_data(input),
_ => Self::deserialize_10x(input),
},
0x01 => Self::deserialize_12x(input), 0x01 => Self::deserialize_12x(input),
0x02 => Self::deserialize_22x(input), 0x02 => Self::deserialize_22x(input),
0x03 => Self::deserialize_32x(input), 0x03 => Self::deserialize_32x(input),
@ -1172,6 +1262,11 @@ impl Serializable for Instruction {
Self::Format45CC { .. } => 8, Self::Format45CC { .. } => 8,
Self::Format4RCC { .. } => 8, Self::Format4RCC { .. } => 8,
Self::Format51L { .. } => 10, Self::Format51L { .. } => 10,
Self::FormatPackedSwitchPayload { targets, .. } => 2 + 2 + 4 + targets.len() * 4,
Self::FormatSparseSwitchPayload { key_targets } => 2 + 2 + key_targets.len() * 8,
Self::FormatFillArrayDataPayload { data, .. } => {
2 + 2 + 4 + data.len() + (data.len() % 2)
}
} }
} }
} }