diff --git a/Cargo.lock b/Cargo.lock index 40b0e4f..ec45c27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,8 +50,8 @@ version = "0.0.0" dependencies = [ "criterion", "pcode-ops", - "sym", "symbolic-pcode", + "sympcode", ] [[package]] @@ -537,9 +537,9 @@ dependencies = [ [[package]] name = "libsla-sys" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336878d48818bcfb2047fe5c3ee3af18e39cbc2d08a6fcb6213801ff81d8e57" +checksum = "9f2662124969c0a607944ebff7402f50371751ea2329a9ce21e5ef9c6c7caf47" dependencies = [ "cxx", "cxx-build", @@ -831,7 +831,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "sym" +name = "symbit" version = "0.1.0" dependencies = [ "aiger-circuit", @@ -849,6 +849,14 @@ dependencies = [ "thiserror", ] +[[package]] +name = "sympcode" +version = "0.1.0" +dependencies = [ + "pcode-ops", + "symbit", +] + [[package]] name = "syn" version = "2.0.111" @@ -879,8 +887,8 @@ dependencies = [ "flexi_logger", "pcode-ops", "sleigh-config", - "sym", "symbolic-pcode", + "sympcode", "z3", ] diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 32e3011..bcbbf7a 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -9,7 +9,7 @@ edition.workspace = true license.workspace = true [dependencies] -symbit = { path = "../crates/sym" } +sympcode = { path = "../crates/sympcode" } pcode-ops = { path = "../crates/pcode-ops" } symbolic-pcode = { path = "../crates/pcode" } diff --git a/benches/emulator.rs b/benches/emulator.rs index ce53bda..fdc0e43 100644 --- a/benches/emulator.rs +++ b/benches/emulator.rs @@ -2,16 +2,16 @@ use std::borrow::Cow; use std::time::Duration; use criterion::{BatchSize, BenchmarkId, Criterion, criterion_group, criterion_main}; -use pcode_ops::PcodeOps; -use symbit::{SymbolicBitVec, SymbolicByte}; +use pcode_ops::convert::PcodeValue; use symbolic_pcode::emulator::{PcodeEmulator, StandardPcodeEmulator}; use symbolic_pcode::libsla::{ Address, AddressSpace, AddressSpaceId, AddressSpaceType, BoolOp, IntOp, IntSign, OpCode, PcodeInstruction, VarnodeData, }; use symbolic_pcode::mem::{GenericMemory, VarnodeDataStore}; +use sympcode::SymPcode; -type Memory = GenericMemory; +type Memory = GenericMemory; const fn processor_space() -> AddressSpace { AddressSpace { @@ -47,7 +47,7 @@ const fn internal_space() -> AddressSpace { } fn setup_copy() -> (Memory, PcodeInstruction) { - let data = SymbolicBitVec::from_le(0x1122334455667788u64); + let data = 0x1122334455667788u64; let mut memory = Memory::default(); let pcode_copy = PcodeInstruction { @@ -73,7 +73,7 @@ fn setup_copy() -> (Memory, PcodeInstruction) { }; memory - .write(&pcode_copy.inputs[0], data) + .write_value(&pcode_copy.inputs[0], data) .expect("failed to initialize memory"); (memory, pcode_copy) @@ -118,13 +118,12 @@ fn setup_load() -> (Memory, PcodeInstruction) { // Write indirect offset to memory let offset = 0x5678u64; - let indirect_offset = SymbolicBitVec::from_le(offset); memory - .write(&pcode_load.inputs[1], indirect_offset) + .write_value(&pcode_load.inputs[1], offset) .expect("failed to write indirect offset"); // Write data to memory - let data = SymbolicBitVec::from_le(0x1122334455667788u64); + let data = 0x1122334455667788u64; let data_varnode = VarnodeData { address: Address { offset, @@ -133,7 +132,7 @@ fn setup_load() -> (Memory, PcodeInstruction) { size: 8, }; memory - .write(&data_varnode, data) + .write_value(&data_varnode, data) .expect("failed to write data"); (memory, pcode_load) @@ -178,15 +177,14 @@ fn setup_store() -> (Memory, PcodeInstruction) { // Write indirect offset to memory let offset = 0x5678u64; - let indirect_offset = SymbolicBitVec::from_le(offset); memory - .write(&instruction.inputs[1], indirect_offset) + .write_value(&instruction.inputs[1], offset) .expect("failed to write indirect offset"); // Write data to memory - let data = SymbolicBitVec::from_le(0x1122334455667788u64); + let data = 0x1122334455667788u64; memory - .write(&instruction.inputs[2], data) + .write_value(&instruction.inputs[2], data) .expect("failed to write data"); (memory, instruction) @@ -230,9 +228,9 @@ fn setup_subpiece() -> (Memory, PcodeInstruction) { let mut memory = Memory::default(); // Write data to memory - let data = SymbolicBitVec::from_le(0x1122334455667788u64); + let data = 0x1122334455667788u64; memory - .write(&instruction.inputs[0], data) + .write_value(&instruction.inputs[0], data) .expect("failed to write data"); (memory, instruction) @@ -303,27 +301,25 @@ fn create_arithmetic_setup_fn(op_code: OpCode) -> impl FnMut() -> (Memory, Pcode }; memory - .write( + .write_value( &instruction.inputs[0], 0xfedcba9876543210u64 .to_le_bytes() .into_iter() - .map(SymbolicByte::from) .take(instruction.inputs[0].size) - .collect(), + .collect::>(), ) .expect("failed to write lhs"); if instruction.inputs.len() > 1 { memory - .write( + .write_value( &instruction.inputs[1], 0x0123456789abcdefu64 .to_le_bytes() .into_iter() - .map(SymbolicByte::from) .take(instruction.inputs[1].size) - .collect(), + .collect::>(), ) .expect("failed to write rhs"); } diff --git a/crates/sym/src/lib.rs b/crates/sym/src/lib.rs index 36207d8..fb51695 100644 --- a/crates/sym/src/lib.rs +++ b/crates/sym/src/lib.rs @@ -2,7 +2,6 @@ mod bit; mod buf; mod convert; mod eval; -mod pcode; mod vec; pub use crate::bit::*; diff --git a/crates/sym/src/pcode.rs b/crates/sym/src/pcode.rs deleted file mode 100644 index ba2ea78..0000000 --- a/crates/sym/src/pcode.rs +++ /dev/null @@ -1,164 +0,0 @@ -use crate::{SymbolicBit, SymbolicBitVec, SymbolicByte}; -use pcode_ops::PcodeOps; - -impl PcodeOps for SymbolicBitVec { - type Byte = SymbolicByte; - type Bit = SymbolicBit; - - fn num_bytes(&self) -> usize { - self.num_bytes() - } - - fn into_le_bytes(self) -> impl ExactSizeIterator { - self.into_bytes().into_iter() - } - - fn add(self, other: Self) -> Self { - self + other - } - - fn unsigned_carry(self, other: Self) -> Self::Bit { - self.unsigned_addition_overflow(other) - } - - fn signed_carry(self, other: Self) -> Self::Bit { - self.signed_addition_overflow(other) - } - - fn negate(self) -> Self { - -self - } - - fn subtract(self, other: Self) -> Self { - self - other - } - - fn borrow(self, other: Self) -> Self::Bit { - self.subtraction_with_borrow(other).1 - } - - fn multiply(self, other: Self) -> Self { - let output_size = self.len(); - self.multiply(other, output_size) - } - - fn unsigned_divide(self, other: Self) -> Self { - other.unsigned_divide(self).0 - } - - fn signed_divide(self, other: Self) -> Self { - other.signed_divide(self).0 - } - - fn unsigned_remainder(self, other: Self) -> Self { - other.unsigned_divide(self).1 - } - - fn signed_remainder(self, other: Self) -> Self { - other.signed_divide(self).1 - } - - fn zero_extend(self, new_size: usize) -> Self { - let num_bits_extension = (u8::BITS as usize * new_size).saturating_sub(self.len()); - if num_bits_extension > 0 { - self.zero_extend(num_bits_extension) - } else { - self - } - } - - fn sign_extend(self, new_size: usize) -> Self { - let num_bits_extension = (u8::BITS as usize * new_size).saturating_sub(self.len()); - if num_bits_extension > 0 { - self.sign_extend(num_bits_extension) - } else { - self - } - } - - fn piece(self, lsb: Self) -> Self { - lsb.concat(self) - } - - fn truncate_to_size(self, new_size: usize) -> Self { - if self.num_bytes() > new_size { - let num_extra_bits = 8 * self.num_bytes() - self.len(); - let trimmed = self.truncate_msb(num_extra_bits); - - let trimmed_len = trimmed.len(); - trimmed.truncate_msb(trimmed_len - 8 * new_size) - } else { - self - } - } - - fn truncate_trailing_bytes(self, amount: u64) -> Self { - let truncate_count = amount.try_into().unwrap_or(usize::MAX); - let truncate_count = truncate_count.saturating_mul(8); - self.truncate_lsb(truncate_count) - } - - fn lsb(self) -> Self::Bit { - self.into_iter().next().unwrap() - } - - fn popcount(self) -> Self { - self.popcount() - } - - fn shift_left(self, other: Self) -> Self { - self << other - } - - fn unsigned_shift_right(self, other: Self) -> Self { - self >> other - } - - fn signed_shift_right(self, other: Self) -> Self { - self.signed_shift_right(other) - } - - fn equals(self, other: Self) -> Self::Bit { - self.equals(other) - } - - fn not_equals(self, other: Self) -> Self::Bit { - !self.equals(other) - } - - fn unsigned_less_than(self, other: Self) -> Self::Bit { - self.less_than(other) - } - - fn signed_less_than(self, other: Self) -> Self::Bit { - self.signed_less_than(other) - } - - fn unsigned_less_than_or_equals(self, other: Self) -> Self::Bit { - self.less_than_eq(other) - } - - fn signed_less_than_or_equals(self, other: Self) -> Self::Bit { - self.signed_less_than_eq(other) - } - - fn signed_greater_than(self, other: Self) -> Self::Bit { - self.signed_greater_than(other) - } - - fn signed_greater_than_or_equals(self, other: Self) -> Self::Bit { - self.signed_greater_than_eq(other) - } - - fn unsigned_greater_than(self, other: Self) -> Self::Bit { - self.greater_than(other) - } - - fn unsigned_greater_than_or_equals(self, other: Self) -> Self::Bit { - self.greater_than_eq(other) - } - - fn fill_bytes_with(bit: Self::Bit, num_bytes: usize) -> Self { - std::iter::repeat_n(bit, u8::BITS as usize * num_bytes).collect() - } -} diff --git a/crates/sym/src/tests.rs b/crates/sym/src/tests.rs index 0b457a4..4f2ed8f 100644 --- a/crates/sym/src/tests.rs +++ b/crates/sym/src/tests.rs @@ -3,4 +3,3 @@ mod bitbuf; mod bitvec; mod convert; mod eval; -mod pcode; diff --git a/crates/sym/src/tests/pcode.rs b/crates/sym/src/tests/pcode.rs deleted file mode 100644 index 59a3a43..0000000 --- a/crates/sym/src/tests/pcode.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::*; -use pcode_ops::validate; - -#[test] -fn validate_pcode() -> validate::Result { - validate::Validator::::validate() -} diff --git a/crates/sympcode/Cargo.toml b/crates/sympcode/Cargo.toml new file mode 100644 index 0000000..193e366 --- /dev/null +++ b/crates/sympcode/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "sympcode" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +symbit = { version = "0.1", path = "../sym" } +pcode-ops = { version = "0.1", path = "../pcode-ops/" } diff --git a/crates/sympcode/src/lib.rs b/crates/sympcode/src/lib.rs new file mode 100644 index 0000000..7526b6e --- /dev/null +++ b/crates/sympcode/src/lib.rs @@ -0,0 +1,4 @@ +mod pcode; + +pub use pcode::{SymBit, SymByte, SymPcode}; +pub use symbit; diff --git a/crates/sympcode/src/pcode.rs b/crates/sympcode/src/pcode.rs new file mode 100644 index 0000000..a62ff6e --- /dev/null +++ b/crates/sympcode/src/pcode.rs @@ -0,0 +1,291 @@ +use pcode_ops::{BitwisePcodeOps, PcodeOps}; +use symbit::{SymbolicBit, SymbolicBitVec, SymbolicByte}; + +#[repr(transparent)] +#[derive(Debug)] +pub struct SymPcode(SymbolicBitVec); + +impl From for SymPcode { + fn from(value: SymbolicBitVec) -> Self { + Self(value) + } +} + +impl From for SymbolicBitVec { + fn from(value: SymPcode) -> Self { + value.0 + } +} + +impl SymPcode { + pub fn with_variables(vars: impl IntoIterator) -> Self { + Self(vars.into_iter().map(SymbolicBit::Variable).collect()) + } + + pub fn into_inner(self) -> SymbolicBitVec { + self.0 + } +} + +impl FromIterator for SymPcode { + fn from_iter>(iter: T) -> Self { + Self(iter.into_iter().map(|b| b.0).collect()) + } +} + +#[repr(transparent)] +#[derive(Clone)] +pub struct SymByte(SymbolicByte); + +impl From for SymByte { + fn from(value: SymBit) -> Self { + Self(value.0.into()) + } +} + +impl TryFrom for u8 { + type Error = >::Error; + + fn try_from(value: SymByte) -> Result { + value.0.try_into() + } +} + +impl From for SymByte { + fn from(value: u8) -> Self { + Self(value.into()) + } +} + +#[repr(transparent)] +#[derive(Clone, Debug)] +pub struct SymBit(SymbolicBit); + +impl From for SymbolicBit { + fn from(value: SymBit) -> Self { + value.0 + } +} + +impl From for SymBit { + fn from(value: bool) -> Self { + Self(value.into()) + } +} + +impl BitwisePcodeOps for SymBit { + fn and(self, rhs: Self) -> Self { + Self(self.0 & rhs.0) + } + + fn not(self) -> Self { + Self(!self.0) + } + + fn or(self, rhs: Self) -> Self { + Self(self.0 | rhs.0) + } + + fn xor(self, rhs: Self) -> Self { + Self(self.0 ^ rhs.0) + } +} + +impl TryFrom for bool { + type Error = >::Error; + + fn try_from(value: SymBit) -> Result { + value.0.try_into() + } +} + +impl PcodeOps for SymPcode { + type Byte = SymByte; + type Bit = SymBit; + + fn num_bytes(&self) -> usize { + self.0.num_bytes() + } + + fn into_le_bytes(self) -> impl ExactSizeIterator { + self.0.into_bytes().into_iter().map(SymByte) + } + + fn add(self, other: Self) -> Self { + Self(self.0 + other.0) + } + + fn unsigned_carry(self, other: Self) -> Self::Bit { + SymBit(self.0.unsigned_addition_overflow(other.0)) + } + + fn signed_carry(self, other: Self) -> Self::Bit { + SymBit(self.0.signed_addition_overflow(other.0)) + } + + fn negate(self) -> Self { + Self(-self.0) + } + + fn subtract(self, other: Self) -> Self { + SymPcode(self.0 - other.0) + } + + fn borrow(self, other: Self) -> Self::Bit { + SymBit(self.0.subtraction_with_borrow(other.0).1) + } + + fn multiply(self, other: Self) -> Self { + let output_size = self.0.len(); + Self(self.0.multiply(other.0, output_size)) + } + + fn unsigned_divide(self, other: Self) -> Self { + Self(other.0.unsigned_divide(self.0).0) + } + + fn signed_divide(self, other: Self) -> Self { + Self(other.0.signed_divide(self.0).0) + } + + fn unsigned_remainder(self, other: Self) -> Self { + Self(other.0.unsigned_divide(self.0).1) + } + + fn signed_remainder(self, other: Self) -> Self { + Self(other.0.signed_divide(self.0).1) + } + + fn zero_extend(self, new_size: usize) -> Self { + let num_bits_extension = (u8::BITS as usize * new_size).saturating_sub(self.0.len()); + if num_bits_extension > 0 { + Self(self.0.zero_extend(num_bits_extension)) + } else { + self + } + } + + fn sign_extend(self, new_size: usize) -> Self { + let num_bits_extension = (u8::BITS as usize * new_size).saturating_sub(self.0.len()); + if num_bits_extension > 0 { + Self(self.0.sign_extend(num_bits_extension)) + } else { + self + } + } + + fn piece(self, lsb: Self) -> Self { + Self(lsb.0.concat(self.0)) + } + + fn truncate_to_size(self, new_size: usize) -> Self { + if self.0.num_bytes() > new_size { + let num_extra_bits = 8 * self.0.num_bytes() - self.0.len(); + let trimmed = self.0.truncate_msb(num_extra_bits); + + let trimmed_len = trimmed.len(); + Self(trimmed.truncate_msb(trimmed_len - 8 * new_size)) + } else { + self + } + } + + fn truncate_trailing_bytes(self, amount: u64) -> Self { + let truncate_count = amount.try_into().unwrap_or(usize::MAX); + let truncate_count = truncate_count.saturating_mul(8); + Self(self.0.truncate_lsb(truncate_count)) + } + + fn lsb(self) -> Self::Bit { + SymBit(self.0.into_iter().next().unwrap()) + } + + fn popcount(self) -> Self { + Self(self.0.popcount()) + } + + fn shift_left(self, other: Self) -> Self { + Self(self.0 << other.0) + } + + fn unsigned_shift_right(self, other: Self) -> Self { + Self(self.0 >> other.0) + } + + fn signed_shift_right(self, other: Self) -> Self { + Self(self.0.signed_shift_right(other.0)) + } + + fn equals(self, other: Self) -> Self::Bit { + SymBit(self.0.equals(other.0)) + } + + fn not_equals(self, other: Self) -> Self::Bit { + SymBit(!self.0.equals(other.0)) + } + + fn unsigned_less_than(self, other: Self) -> Self::Bit { + SymBit(self.0.less_than(other.0)) + } + + fn signed_less_than(self, other: Self) -> Self::Bit { + SymBit(self.0.signed_less_than(other.0)) + } + + fn unsigned_less_than_or_equals(self, other: Self) -> Self::Bit { + SymBit(self.0.less_than_eq(other.0)) + } + + fn signed_less_than_or_equals(self, other: Self) -> Self::Bit { + SymBit(self.0.signed_less_than_eq(other.0)) + } + + fn signed_greater_than(self, other: Self) -> Self::Bit { + SymBit(self.0.signed_greater_than(other.0)) + } + + fn signed_greater_than_or_equals(self, other: Self) -> Self::Bit { + SymBit(self.0.signed_greater_than_eq(other.0)) + } + + fn unsigned_greater_than(self, other: Self) -> Self::Bit { + SymBit(self.0.greater_than(other.0)) + } + + fn unsigned_greater_than_or_equals(self, other: Self) -> Self::Bit { + SymBit(self.0.greater_than_eq(other.0)) + } + + fn fill_bytes_with(bit: Self::Bit, num_bytes: usize) -> Self { + Self(std::iter::repeat_n(bit.0, u8::BITS as usize * num_bytes).collect()) + } +} + +impl BitwisePcodeOps for SymPcode { + fn and(self, rhs: Self) -> Self { + Self(self.0 & rhs.0) + } + + fn not(self) -> Self { + Self(!self.0) + } + + fn or(self, rhs: Self) -> Self { + Self(self.0 | rhs.0) + } + + fn xor(self, rhs: Self) -> Self { + Self(self.0 ^ rhs.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use pcode_ops::validate; + + #[test] + fn validate_pcode() -> validate::Result { + validate::Validator::::validate() + } +} diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 708ea0a..afdb0d9 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -12,7 +12,7 @@ edition.workspace = true aiger-circuit = "1" symbolic-pcode = { path = "../crates/pcode" } pcode-ops = { path = "../crates/pcode-ops" } -symbit = { path = "../crates/sym" } +sympcode = { path = "../crates/sympcode" } [dev-dependencies] flexi_logger = "0.31" diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 3e86856..c18f058 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,10 +1,11 @@ use std::path::Path; use std::sync::OnceLock; +use pcode_ops::convert::PcodeValue; use sleigh_config::{processor_aarch64, processor_x86}; -use symbit::{SymbolicBit, SymbolicBitVec, SymbolicByte}; use symbolic_pcode::libsla::{self, Address, GhidraSleigh, Sleigh, VarnodeData}; use symbolic_pcode::mem::{GenericMemory, VarnodeDataStore}; +use sympcode::SymPcode; static LOGGER_INIT: OnceLock = OnceLock::new(); @@ -20,7 +21,7 @@ pub fn initialize_logger() -> &'static flexi_logger::LoggerHandle { pub const INITIAL_STACK: u64 = 0x8000000000; pub const EXIT_IP_ADDR: u64 = 0xFEEDBEEF0BADF00D; -pub type Memory = GenericMemory; +pub type Memory = GenericMemory; pub fn x86_64_sleigh() -> libsla::Result { let sleigh = GhidraSleigh::builder() @@ -71,9 +72,12 @@ pub fn memory_with_image( let offset = segment.p_offset as usize; let file_size = segment.p_filesz as usize; memory - .write( + .write_value( &data_location, - data[offset..offset + file_size].iter().copied().collect(), + data[offset..offset + file_size] + .iter() + .copied() + .collect::>(), ) .expect("failed to write image section into memory"); @@ -98,14 +102,17 @@ pub fn memory_with_image( vaddr = segment.p_vaddr ); memory - .write(&zeros_location, zeros.into_iter().collect()) + .write_value( + &zeros_location, + zeros.into_iter().collect::>(), + ) .expect("failed to write zeros into memory"); } } // Init RIP to entry memory - .write(pc_register, elf.ehdr.e_entry.into()) + .write_value(pc_register, elf.ehdr.e_entry) .expect("failed to initialize PC register"); memory @@ -137,7 +144,7 @@ pub fn init_registers_x86_64(sleigh: &impl Sleigh, memory: &mut Memory) { .register_from_name("DF") .expect("failed to get DF register"); memory - .write(&df_register, 0u8.into()) + .write_value(&df_register, 0u8) .expect("failed to init DF register"); } @@ -154,7 +161,7 @@ pub fn init_registers_aarch64(sleigh: &impl Sleigh, memory: &mut Memory) { .register_from_name("x30") .expect("invalid link register"); memory - .write(&link_register, EXIT_IP_ADDR.into()) + .write_value(&link_register, EXIT_IP_ADDR) .expect("failed to initialize link register"); // Initialize system register `dczid_el0` @@ -175,7 +182,7 @@ pub fn init_registers_aarch64(sleigh: &impl Sleigh, memory: &mut Memory) { .register_from_name("dczid_el0") .expect("unknown register"); memory - .write(&dczid_el0, 0x10u64.into()) + .write_value(&dczid_el0, 0x10u64) .expect("failed to initialize dczid_el0"); } @@ -187,34 +194,22 @@ fn init_registers( ) { let mut bitvar = 0; for register_name in registers.into_iter() { - let mut bytes = Vec::with_capacity(8); - for _ in 0..8 { - let byte: SymbolicByte = [ - SymbolicBit::Variable(bitvar), - SymbolicBit::Variable(bitvar + 1), - SymbolicBit::Variable(bitvar + 2), - SymbolicBit::Variable(bitvar + 3), - SymbolicBit::Variable(bitvar + 4), - SymbolicBit::Variable(bitvar + 5), - SymbolicBit::Variable(bitvar + 6), - SymbolicBit::Variable(bitvar + 7), - ] - .into(); - bytes.push(byte); - bitvar += 8; - } - let register = sleigh .register_from_name(®ister_name) .unwrap_or_else(|err| panic!("invalid register {register_name}: {err}")); + let num_bits = register.size * 8; memory - .write(®ister, bytes.into_iter().collect()) + .write( + ®ister, + SymPcode::with_variables(bitvar..bitvar + num_bits), + ) .unwrap_or_else(|err| panic!("failed to write register {register_name}: {err}")); + bitvar += num_bits; } // Init stack register to stack address memory - .write(stack_register, INITIAL_STACK.into()) + .write_value(stack_register, INITIAL_STACK) .expect("failed to initialize stack register"); } diff --git a/tests/hello_world.rs b/tests/hello_world.rs index 73c342e..813a137 100644 --- a/tests/hello_world.rs +++ b/tests/hello_world.rs @@ -47,8 +47,15 @@ fn hello_world_x86_linux() -> processor::Result<()> { let mut processor = Processor::new(memory, emulator, handler); loop { + let print_pcode = matches!(processor.state(), ProcessorState::Decode(_)); processor.step(sleigh.as_ref())?; + if print_pcode && let ProcessorState::Execute(e) = processor.state() { + for instr in &e.pcode().instructions { + println!("{instr}"); + } + } + // Debug if matches!(processor.state(), ProcessorState::Decode(_)) { let disassembly = processor.disassemble(sleigh.as_ref())?; diff --git a/tests/hello_world/util.rs b/tests/hello_world/util.rs index 44ce622..ed04f11 100644 --- a/tests/hello_world/util.rs +++ b/tests/hello_world/util.rs @@ -1,6 +1,7 @@ -use symbit::SymbolicBitVec; use symbolic_pcode::libsla::{Address, Sleigh, VarnodeData}; use symbolic_pcode::mem::VarnodeDataStore; +use sympcode::SymPcode; +use sympcode::symbit::SymbolicBitVec; use crate::common::{INITIAL_STACK, Memory}; @@ -21,7 +22,10 @@ pub fn initialize_libc_stack(memory: &mut Memory, sleigh: &impl Sleigh) { size: 8, }; memory - .write(&argc, SymbolicBitVec::constant(1, u64::BITS as usize)) + .write_value( + &argc, + SymPcode::from(SymbolicBitVec::constant(1, u64::BITS as usize)), + ) .expect("failed to initialize argc on stack"); // The argv list must be terminated by null pointer. Setting program name to null AND @@ -37,7 +41,10 @@ pub fn initialize_libc_stack(memory: &mut Memory, sleigh: &impl Sleigh) { size: 16, }; memory - .write(&argv, SymbolicBitVec::constant(0, (2 * u64::BITS) as usize)) + .write_value( + &argv, + SymPcode::from(SymbolicBitVec::constant(0, (2 * u64::BITS) as usize)), + ) .expect("failed to initialize argv"); let envp = VarnodeData { @@ -48,7 +55,10 @@ pub fn initialize_libc_stack(memory: &mut Memory, sleigh: &impl Sleigh) { size: 8, }; memory - .write(&envp, SymbolicBitVec::constant(0, u64::BITS as usize)) + .write_value( + &envp, + SymPcode::from(SymbolicBitVec::constant(0, u64::BITS as usize)), + ) .expect("failed to initialize envp"); // musl targets initialize the libc pagesize using aux[AT_PAGESZ]. For architectures without a @@ -66,23 +76,26 @@ pub fn initialize_libc_stack(memory: &mut Memory, sleigh: &impl Sleigh) { // The index for AT_PAGESZ let at_pagesz = 6; memory - .write( + .write_value( &auxv, - SymbolicBitVec::constant(at_pagesz, u64::BITS as usize), + SymPcode::from(SymbolicBitVec::constant(at_pagesz, u64::BITS as usize)), ) .expect("failed to write AT_PAGESZ into auxv"); auxv.address.offset += auxv.size as u64; let page_size = 4096; memory - .write( + .write_value( &auxv, - SymbolicBitVec::constant(page_size, u64::BITS as usize), + SymPcode::from(SymbolicBitVec::constant(page_size, u64::BITS as usize)), ) .expect("failed to write page size into auxv"); auxv.address.offset += auxv.size as u64; memory - .write(&auxv, SymbolicBitVec::constant(0, u64::BITS as usize)) + .write_value( + &auxv, + SymPcode::from(SymbolicBitVec::constant(0, u64::BITS as usize)), + ) .expect("failed to initialize auxv"); } diff --git a/tests/pcode_coverage.rs b/tests/pcode_coverage.rs index c35fd4d..88a5a9e 100644 --- a/tests/pcode_coverage.rs +++ b/tests/pcode_coverage.rs @@ -46,7 +46,7 @@ fn pcode_coverage() -> processor::Result<()> { size: common::EXIT_IP_ADDR.to_le_bytes().len(), }; memory - .write(&stack_addr, common::EXIT_IP_ADDR.into()) + .write_value(&stack_addr, common::EXIT_IP_ADDR) .expect("failed to initialize stack"); let handler = ProcessorHandlerX86::new(&sleigh); @@ -73,7 +73,7 @@ fn pcode_coverage() -> processor::Result<()> { let instruction_pointer: u64 = processor .memory() - .read(&rip)? + .read_value(&rip)? .try_into() .expect("failed to concretize RIP"); @@ -89,7 +89,7 @@ fn pcode_coverage() -> processor::Result<()> { .expect("failed to get RAX register"); let return_value: u64 = processor .memory() - .read(&rax)? + .read_value(&rax)? .try_into() .expect("failed to concretize RAX"); assert_eq!( @@ -152,7 +152,7 @@ fn pcode_coverage_aarch64() -> processor::Result<()> { size: common::EXIT_IP_ADDR.to_le_bytes().len(), }; memory - .write(&stack_addr, common::EXIT_IP_ADDR.into()) + .write_value(&stack_addr, common::EXIT_IP_ADDR) .expect("failed to initialize stack"); let handler = arch::aarch64::processor::ProcessorHandler::new(&sleigh); @@ -179,7 +179,7 @@ fn pcode_coverage_aarch64() -> processor::Result<()> { let instruction_pointer: u64 = processor .memory() - .read(&pc)? + .read_value(&pc)? .try_into() .expect("failed to concretize pc register"); @@ -195,7 +195,7 @@ fn pcode_coverage_aarch64() -> processor::Result<()> { .expect("failed to get x0 register"); let return_value: u64 = processor .memory() - .read(&return_register)? + .read_value(&return_register)? .try_into() .expect("failed to concretize return register"); assert_eq!( diff --git a/tests/x86_64_emulator.rs b/tests/x86_64_emulator.rs index af4b9c4..1883a33 100644 --- a/tests/x86_64_emulator.rs +++ b/tests/x86_64_emulator.rs @@ -1,5 +1,4 @@ -use pcode_ops::PcodeOps; -use symbit::{self, Evaluator, SymbolicBit, SymbolicBitVec, VariableAssignments}; +use pcode_ops::{PcodeOps, convert::PcodeValue}; use symbolic_pcode::{ arch::x86::processor::ProcessorHandlerX86, emulator::StandardPcodeEmulator, @@ -7,6 +6,8 @@ use symbolic_pcode::{ mem::{MemoryTree, VarnodeDataStore}, processor::{self, BranchingProcessor, Processor, ProcessorState}, }; +use sympcode::SymPcode; +use sympcode::symbit::{self, Evaluator, SymbolicBit, SymbolicBitVec, VariableAssignments}; use crate::common::{self, Memory, x86_64_sleigh}; @@ -20,7 +21,7 @@ fn x86_64_registers() { .register_from_name(name) .unwrap_or_else(|_| panic!("invalid register {name}")); memory - .write(®ister, data.iter().copied().collect()) + .write_value(®ister, data.iter().copied().collect::>()) .unwrap_or_else(|_| panic!("failed to write register {name}")); }; @@ -29,7 +30,7 @@ fn x86_64_registers() { .register_from_name(name) .unwrap_or_else(|_| panic!("invalid register {name}")); memory - .read(®ister) + .read_value(®ister) .unwrap_or_else(|_| panic!("failed to read register {name}")) }; @@ -151,19 +152,22 @@ fn doubler_32b() -> processor::Result<()> { .register_from_name(name) .unwrap_or_else(|err| panic!("invalid register {name}: {err}")); memory - .write(®ister, data.iter().copied().collect()) + .write_value(®ister, data.iter().copied().collect::>()) .unwrap_or_else(|err| panic!("failed to write register {name}: {err}")); }; let code_space = sleigh.default_code_space(); let code = b"\x55\x48\x89\xe5\x89\x7d\xfc\x8b\x45\xfc\x01\xc0\x5d\xc3\x00\x00"; let destination = VarnodeData::new(Address::new(code_space, base_addr), code.len()); - memory.write(&destination, code.into_iter().copied().collect())?; + memory.write_value( + &destination, + code.into_iter().copied().collect::>(), + )?; write_register(&mut memory, "RSP", b"\x00\x01\x01\x01\x01\x01\x01\x00"); write_register(&mut memory, "RBP", b"\x00\x02\x02\x02\x02\x02\x02\x00"); - memory.write( + memory.write_value( &VarnodeData::new( Address::new( sleigh @@ -173,7 +177,7 @@ fn doubler_32b() -> processor::Result<()> { ), 8, ), - 0x66778899aabbccddu64.into(), + 0x66778899aabbccddu64, )?; let initial_value: u32 = 0x99; @@ -197,7 +201,7 @@ fn doubler_32b() -> processor::Result<()> { .expect("failed to get RIP register"); let rip_value: u64 = processor .memory() - .read(&rip)? + .read_value(&rip)? .try_into() .expect("failed to convert rip value to u64"); assert_eq!(rip_value, 0x66778899aabbccdd, "return address on stack"); @@ -207,7 +211,7 @@ fn doubler_32b() -> processor::Result<()> { .expect("failed to get RAX register"); let rax_value: u64 = processor .memory() - .read(&rax)? + .read_value(&rax)? .try_into() .expect("failed to convert rax value to u64"); assert_eq!( @@ -230,7 +234,7 @@ fn z3_integration() -> processor::Result<()> { .register_from_name(name) .unwrap_or_else(|_| panic!("invalid register {name}")); memory - .write(®ister, data.iter().copied().collect()) + .write_value(®ister, data.iter().copied().collect::>()) .unwrap_or_else(|_| panic!("failed to write register {name}")); }; @@ -252,12 +256,12 @@ fn z3_integration() -> processor::Result<()> { ]; let code_offset = 0; - memory.write( + memory.write_value( &VarnodeData::new( Address::new(sleigh.default_code_space(), code_offset), program_bytes.len(), ), - program_bytes.into_iter().collect(), + program_bytes.into_iter().collect::>(), )?; write_register(&mut memory, "RIP", &code_offset.to_le_bytes()); @@ -266,7 +270,7 @@ fn z3_integration() -> processor::Result<()> { write_register(&mut memory, "RBP", &common::INITIAL_STACK.to_le_bytes()); // Put EXIT_IP_ADDR onto the stack. The final RET will trigger this. - memory.write( + memory.write_value( &VarnodeData::new( Address::new( sleigh @@ -276,10 +280,10 @@ fn z3_integration() -> processor::Result<()> { ), common::EXIT_IP_ADDR.to_le_bytes().len(), ), - common::EXIT_IP_ADDR.into(), + common::EXIT_IP_ADDR, )?; - let input_value = SymbolicBitVec::with_size(32); + let input_value = SymPcode::with_variables(0..32); // Input register is EDI let name = "EDI"; @@ -287,7 +291,7 @@ fn z3_integration() -> processor::Result<()> { .register_from_name(name) .unwrap_or_else(|err| panic!("invalid register {name}: {err}")); memory - .write(®ister, input_value) + .write_value(®ister, input_value) .unwrap_or_else(|err| panic!("failed to write register {name}: {err}")); let handler = ProcessorHandlerX86::new(&sleigh); @@ -309,7 +313,7 @@ fn z3_integration() -> processor::Result<()> { let rip_value: u64 = processors[i] .processor() .memory() - .read(&rip) + .read_value(&rip) .expect("failed to read RIP") .try_into() .expect("failed to concretize RIP"); @@ -337,8 +341,8 @@ fn z3_integration() -> processor::Result<()> { finished.iter().map(|p| p.processor().memory()), std::iter::empty(), ); - let result = memory_tree.read(&eax)?; - let result: SymbolicBitVec = result.into_iter().collect(); + + let result = memory_tree.read(&eax)?.into_inner(); let assertion = result.equals(SymbolicBitVec::constant(8, 32)); let aiger = aiger_circuit::Aiger::with_outputs(std::iter::once(&assertion)); @@ -443,7 +447,7 @@ fn take_the_path_not_taken() -> processor::Result<()> { .register_from_name(name) .unwrap_or_else(|err| panic!("invalid register {name}: {err}")); memory - .write(®ister, data.iter().copied().collect()) + .write_value(®ister, data.iter().copied().collect::>()) .unwrap_or_else(|err| panic!("failed to write register {name}: {err}")); }; @@ -465,12 +469,12 @@ fn take_the_path_not_taken() -> processor::Result<()> { ]; let code_offset = 0; - memory.write( + memory.write_value( &VarnodeData::new( Address::new(sleigh.default_code_space(), code_offset), program_bytes.len(), ), - program_bytes.into_iter().collect(), + program_bytes.into_iter().collect::>(), )?; write_register(&mut memory, "RIP", &code_offset.to_le_bytes()); @@ -479,7 +483,7 @@ fn take_the_path_not_taken() -> processor::Result<()> { write_register(&mut memory, "RBP", &common::INITIAL_STACK.to_le_bytes()); // Put EXIT_IP_ADDR onto the stack. The final RET will trigger this. - memory.write( + memory.write_value( &VarnodeData::new( Address::new( sleigh @@ -489,7 +493,7 @@ fn take_the_path_not_taken() -> processor::Result<()> { ), common::EXIT_IP_ADDR.to_le_bytes().len(), ), - common::EXIT_IP_ADDR.into(), + common::EXIT_IP_ADDR, )?; // Create symbolic input @@ -511,7 +515,7 @@ fn take_the_path_not_taken() -> processor::Result<()> { .register_from_name(name) .unwrap_or_else(|err| panic!("invalid register {name}: {err}")); memory - .write(®ister, input_value) + .write_value(®ister, SymPcode::from(input_value)) .unwrap_or_else(|err| panic!("failed to write register {name}: {err}")); let handler = ProcessorHandlerX86::new(&sleigh); @@ -524,13 +528,17 @@ fn take_the_path_not_taken() -> processor::Result<()> { loop { if let Err(e) = processor.step(&sleigh) { if let processor::Error::SymbolicBranch { condition_origin } = &e { - let condition = processor.memory().read(condition_origin)?; + let condition = processor + .memory() + .read_value(condition_origin)? + .into_inner() + .into_inner(); let evaluation = evaluator - .evaluate(&processor.memory().read_bit(condition_origin)?) + .evaluate(&processor.memory().read_bit(condition_origin)?.into()) .response .expect("evaluation should be concrete"); if evaluation { - branches.push(condition.not_equals(0u8.into())); + branches.push(!condition.equals(0u8.into())); } else { branches.push(condition.equals(0u8.into())); } @@ -544,7 +552,7 @@ fn take_the_path_not_taken() -> processor::Result<()> { // Exit loop when we reach the magic RIP let instruction_pointer: u64 = processor .memory() - .read(&rip)? + .read_value(&rip)? .try_into() .expect("failed to concretize RIP"); if instruction_pointer == common::EXIT_IP_ADDR