#![allow(clippy::unwrap_used, clippy::expect_used)]
#[derive(Default)]
pub struct LimeBuilder {
ranges: Vec<(u64, Vec<u8>)>,
}
impl LimeBuilder {
pub fn new() -> Self {
Self { ranges: Vec::new() }
}
pub fn add_range(mut self, start: u64, data: &[u8]) -> Self {
self.ranges.push((start, data.to_vec()));
self
}
pub fn build(self) -> Vec<u8> {
const MAGIC: u32 = 0x4C694D45;
const VERSION: u32 = 1;
let mut out = Vec::new();
for (start, data) in &self.ranges {
let e_addr = start + data.len() as u64 - 1; out.extend_from_slice(&MAGIC.to_le_bytes());
out.extend_from_slice(&VERSION.to_le_bytes());
out.extend_from_slice(&start.to_le_bytes());
out.extend_from_slice(&e_addr.to_le_bytes());
out.extend_from_slice(&0u64.to_le_bytes()); out.extend_from_slice(data);
}
out
}
}
#[derive(Default)]
pub struct AvmlBuilder {
ranges: Vec<(u64, Vec<u8>)>,
}
impl AvmlBuilder {
pub fn new() -> Self {
Self { ranges: Vec::new() }
}
pub fn add_range(mut self, start: u64, data: &[u8]) -> Self {
self.ranges.push((start, data.to_vec()));
self
}
pub fn build(self) -> Vec<u8> {
const MAGIC: u32 = 0x4C4D5641;
const VERSION: u32 = 2;
let mut out = Vec::new();
for (start, data) in &self.ranges {
let e_addr = start + data.len() as u64; let uncompressed_size = data.len() as u64;
let mut encoder = snap::raw::Encoder::new();
let compressed = encoder.compress_vec(data).expect("snappy compress");
out.extend_from_slice(&MAGIC.to_le_bytes());
out.extend_from_slice(&VERSION.to_le_bytes());
out.extend_from_slice(&start.to_le_bytes());
out.extend_from_slice(&e_addr.to_le_bytes());
out.extend_from_slice(&0u64.to_le_bytes()); out.extend_from_slice(&compressed);
out.extend_from_slice(&uncompressed_size.to_le_bytes()); }
out
}
}
pub struct CrashDumpBuilder {
runs: Vec<(u64, Vec<u8>)>,
cr3: u64,
ps_active_process_head: u64,
ps_loaded_module_list: u64,
kd_debugger_data_block: u64,
machine_type: u32,
num_processors: u32,
dump_type: u32,
system_time: u64,
}
impl Default for CrashDumpBuilder {
fn default() -> Self {
Self {
runs: Vec::new(),
cr3: 0x0018_7000,
ps_active_process_head: 0xFFFFF802_1A2B3C40,
ps_loaded_module_list: 0xFFFFF802_1A2B3D60,
kd_debugger_data_block: 0xFFFFF802_1A000000,
machine_type: 0x8664, num_processors: 4,
dump_type: 0x01, system_time: 0x01DA_5678_9ABC_DEF0,
}
}
}
impl CrashDumpBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn add_run(mut self, base_page: u64, data: &[u8]) -> Self {
assert!(
data.len() % 4096 == 0,
"run data length must be a multiple of 4096"
);
self.runs.push((base_page, data.to_vec()));
self
}
pub fn cr3(mut self, val: u64) -> Self {
self.cr3 = val;
self
}
pub fn ps_active_process_head(mut self, val: u64) -> Self {
self.ps_active_process_head = val;
self
}
pub fn ps_loaded_module_list(mut self, val: u64) -> Self {
self.ps_loaded_module_list = val;
self
}
pub fn kd_debugger_data_block(mut self, val: u64) -> Self {
self.kd_debugger_data_block = val;
self
}
pub fn machine_type(mut self, val: u32) -> Self {
self.machine_type = val;
self
}
pub fn num_processors(mut self, val: u32) -> Self {
self.num_processors = val;
self
}
pub fn dump_type(mut self, val: u32) -> Self {
self.dump_type = val;
self
}
pub fn system_time(mut self, val: u64) -> Self {
self.system_time = val;
self
}
pub fn build(self) -> Vec<u8> {
const PAGE_MAGIC: u32 = 0x4547_4150; const DU64_SIG: u32 = 0x3436_5544; const HEADER_SIZE: usize = 0x2000; const PAGE_SIZE: usize = 4096;
let mut header = vec![0u8; HEADER_SIZE];
header[0x000..0x004].copy_from_slice(&PAGE_MAGIC.to_le_bytes());
header[0x004..0x008].copy_from_slice(&DU64_SIG.to_le_bytes());
header[0x010..0x018].copy_from_slice(&self.cr3.to_le_bytes());
header[0x020..0x028].copy_from_slice(&self.ps_loaded_module_list.to_le_bytes());
header[0x028..0x030].copy_from_slice(&self.ps_active_process_head.to_le_bytes());
header[0x030..0x034].copy_from_slice(&self.machine_type.to_le_bytes());
header[0x034..0x038].copy_from_slice(&self.num_processors.to_le_bytes());
header[0x080..0x088].copy_from_slice(&self.kd_debugger_data_block.to_le_bytes());
let num_runs = self.runs.len() as u32;
let total_pages: u64 = self
.runs
.iter()
.map(|(_, d)| (d.len() / PAGE_SIZE) as u64)
.sum();
header[0x088..0x08C].copy_from_slice(&num_runs.to_le_bytes());
header[0x08C..0x090].copy_from_slice(&0u32.to_le_bytes());
header[0x090..0x098].copy_from_slice(&total_pages.to_le_bytes());
for (i, (base_page, data)) in self.runs.iter().enumerate() {
let page_count = (data.len() / PAGE_SIZE) as u64;
let off = 0x098 + i * 16;
header[off..off + 8].copy_from_slice(&base_page.to_le_bytes());
header[off + 8..off + 16].copy_from_slice(&page_count.to_le_bytes());
}
header[0xF98..0xF9C].copy_from_slice(&self.dump_type.to_le_bytes());
header[0xFA8..0xFB0].copy_from_slice(&self.system_time.to_le_bytes());
let is_bitmap = self.dump_type == 0x02 || self.dump_type == 0x05;
if is_bitmap {
self.build_bitmap(header)
} else {
self.build_run_based(header)
}
}
fn build_run_based(self, mut out: Vec<u8>) -> Vec<u8> {
for (_, data) in &self.runs {
out.extend_from_slice(data);
}
out
}
fn build_bitmap(self, mut out: Vec<u8>) -> Vec<u8> {
const DUMP_VALID: u32 = 0x504D_5544; const PAGE_SIZE: usize = 4096;
let max_pfn: u64 = self
.runs
.iter()
.map(|(base, data)| base + (data.len() / PAGE_SIZE) as u64)
.max()
.unwrap_or(0);
let bitmap_bits = max_pfn as usize;
let bitmap_bytes = bitmap_bits.div_ceil(8);
let bitmap_bytes_aligned = (bitmap_bytes + 7) & !7;
let mut bitmap = vec![0u8; bitmap_bytes_aligned];
for (base_page, data) in &self.runs {
let page_count = data.len() / PAGE_SIZE;
for p in 0..page_count {
let pfn = *base_page as usize + p;
bitmap[pfn / 8] |= 1 << (pfn % 8);
}
}
let total_set_pages: u32 = self
.runs
.iter()
.map(|(_, d)| (d.len() / PAGE_SIZE) as u32)
.sum();
let summary_header_size: u32 = 16; let data_offset = summary_header_size as usize + bitmap_bytes_aligned;
out.extend_from_slice(&DUMP_VALID.to_le_bytes());
out.extend_from_slice(&(data_offset as u32).to_le_bytes());
out.extend_from_slice(&(bitmap_bytes_aligned as u32).to_le_bytes());
out.extend_from_slice(&total_set_pages.to_le_bytes());
out.extend_from_slice(&bitmap);
let mut pfn_data: Vec<(u64, &[u8])> = Vec::new();
for (base_page, data) in &self.runs {
let page_count = data.len() / PAGE_SIZE;
for p in 0..page_count {
let pfn = base_page + p as u64;
let start = p * PAGE_SIZE;
pfn_data.push((pfn, &data[start..start + PAGE_SIZE]));
}
}
pfn_data.sort_by_key(|(pfn, _)| *pfn);
for (_, page) in &pfn_data {
out.extend_from_slice(page);
}
out
}
}
#[derive(Default)]
pub struct ElfCoreBuilder {
segments: Vec<(u64, Vec<u8>)>,
}
impl ElfCoreBuilder {
pub fn new() -> Self {
Self {
segments: Vec::new(),
}
}
pub fn add_segment(mut self, paddr: u64, data: &[u8]) -> Self {
self.segments.push((paddr, data.to_vec()));
self
}
pub fn build(self) -> Vec<u8> {
let ehdr_size: usize = 64;
let phdr_size: usize = 56;
let phdr_count = self.segments.len();
let phdr_total = phdr_count * phdr_size;
let data_start = (ehdr_size + phdr_total).div_ceil(0x1000) * 0x1000;
let mut out = vec![0u8; data_start];
out[0..4].copy_from_slice(&[0x7F, b'E', b'L', b'F']);
out[4] = 2; out[5] = 1; out[6] = 1; out[16..18].copy_from_slice(&4u16.to_le_bytes()); out[18..20].copy_from_slice(&62u16.to_le_bytes()); out[20..24].copy_from_slice(&1u32.to_le_bytes()); out[32..40].copy_from_slice(&(ehdr_size as u64).to_le_bytes()); out[52..54].copy_from_slice(&(ehdr_size as u16).to_le_bytes()); out[54..56].copy_from_slice(&(phdr_size as u16).to_le_bytes()); out[56..58].copy_from_slice(&(phdr_count as u16).to_le_bytes());
let mut current_offset = data_start;
for (i, (paddr, data)) in self.segments.iter().enumerate() {
let phdr_off = ehdr_size + i * phdr_size;
out[phdr_off..phdr_off + 4].copy_from_slice(&1u32.to_le_bytes()); out[phdr_off + 4..phdr_off + 8].copy_from_slice(&6u32.to_le_bytes()); out[phdr_off + 8..phdr_off + 16]
.copy_from_slice(&(current_offset as u64).to_le_bytes());
out[phdr_off + 24..phdr_off + 32].copy_from_slice(&paddr.to_le_bytes());
out[phdr_off + 32..phdr_off + 40].copy_from_slice(&(data.len() as u64).to_le_bytes());
out[phdr_off + 40..phdr_off + 48].copy_from_slice(&(data.len() as u64).to_le_bytes());
out[phdr_off + 48..phdr_off + 56].copy_from_slice(&0x1000u64.to_le_bytes());
out.resize(current_offset + data.len(), 0);
out[current_offset..current_offset + data.len()].copy_from_slice(data);
current_offset += data.len();
}
out
}
}
#[derive(Default)]
pub struct VmwareStateBuilder {
memory_regions: Vec<(u64, Vec<u8>)>,
cr3: Option<u64>,
}
impl VmwareStateBuilder {
pub fn new() -> Self {
Self {
memory_regions: Vec::new(),
cr3: None,
}
}
pub fn add_region(mut self, paddr: u64, data: &[u8]) -> Self {
self.memory_regions.push((paddr, data.to_vec()));
self
}
pub fn cr3(mut self, cr3: u64) -> Self {
self.cr3 = Some(cr3);
self
}
pub fn build(self) -> Vec<u8> {
let group_count: u32 = if self.cr3.is_some() { 2 } else { 1 };
let header_size = 12usize;
let group_entry_size = 80usize;
let groups_size = group_count as usize * group_entry_size;
let mut out = Vec::new();
out.extend_from_slice(&0xBED2BED0u32.to_le_bytes()); out.extend_from_slice(&0u32.to_le_bytes()); out.extend_from_slice(&group_count.to_le_bytes());
let groups_start = out.len();
out.resize(header_size + groups_size, 0);
let memory_tags_offset = out.len() as u64;
for (paddr, data) in &self.memory_regions {
let name = b"regionPPN";
out.push(0x06); out.push(name.len() as u8);
out.extend_from_slice(name);
out.extend_from_slice(&8u32.to_le_bytes()); out.extend_from_slice(&paddr.to_le_bytes());
let name = b"regionBytes";
out.push(0x06); out.push(name.len() as u8);
out.extend_from_slice(name);
let data_len = data.len() as u32;
out.extend_from_slice(&data_len.to_le_bytes()); out.extend_from_slice(data);
}
out.push(0x00);
{
let entry_offset = groups_start;
let name = b"memory";
out[entry_offset..entry_offset + name.len()].copy_from_slice(name);
let tags_off_pos = entry_offset + 64;
out[tags_off_pos..tags_off_pos + 8].copy_from_slice(&memory_tags_offset.to_le_bytes());
}
if let Some(cr3_val) = self.cr3 {
let cpu_tags_offset = out.len() as u64;
let name = b"CR3";
out.push(0x46); out.push(name.len() as u8);
out.extend_from_slice(name);
out.push(0x00); out.push(0x03); out.extend_from_slice(&cr3_val.to_le_bytes());
out.push(0x00);
let entry_offset = groups_start + group_entry_size;
let name = b"cpu";
out[entry_offset..entry_offset + name.len()].copy_from_slice(name);
let tags_off_pos = entry_offset + 64;
out[tags_off_pos..tags_off_pos + 8].copy_from_slice(&cpu_tags_offset.to_le_bytes());
}
out
}
}
fn xpress_compress_safe(data: &[u8]) -> Vec<u8> {
if let Ok(compressed) = lzxpress::data::compress(data) {
if let Ok(decompressed) = lzxpress::data::decompress(&compressed) {
if decompressed == data {
return compressed;
}
}
}
let mut out = Vec::with_capacity(data.len() + data.len() / 32 * 4 + 8);
let mut pos = 0;
while pos < data.len() {
let chunk_len = (data.len() - pos).min(32);
out.extend_from_slice(&0u32.to_le_bytes());
out.extend_from_slice(&data[pos..pos + chunk_len]);
pos += chunk_len;
}
out
}
pub struct HiberfilBuilder {
pages: Vec<(u64, [u8; 4096])>,
cr3: u64,
}
impl Default for HiberfilBuilder {
fn default() -> Self {
Self {
pages: Vec::new(),
cr3: 0x1ab000,
}
}
}
impl HiberfilBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn cr3(mut self, cr3: u64) -> Self {
self.cr3 = cr3;
self
}
pub fn add_page(mut self, pfn: u64, data: &[u8; 4096]) -> Self {
self.pages.push((pfn, *data));
self
}
pub fn build(self) -> Vec<u8> {
const PAGE_SIZE: usize = 4096;
const HIBR_MAGIC: u32 = 0x7262_6968; const LENGTH_SELF_64: u32 = 256;
const XPRESS_SIG: [u8; 8] = [0x81, 0x81, b'x', b'p', b'r', b'e', b's', b's'];
const BLOCK_HEADER_SIZE: usize = 0x20;
let mut out = vec![0u8; 3 * PAGE_SIZE];
out[0x00..0x04].copy_from_slice(&HIBR_MAGIC.to_le_bytes());
out[0x0C..0x10].copy_from_slice(&LENGTH_SELF_64.to_le_bytes());
out[0x68..0x70].copy_from_slice(&2u64.to_le_bytes());
let cr3_offset = PAGE_SIZE + 0x28;
out[cr3_offset..cr3_offset + 8].copy_from_slice(&self.cr3.to_le_bytes());
let table_base = 2 * PAGE_SIZE;
let mut table_offset = 0usize;
for (pfn, _) in &self.pages {
out[table_base + table_offset..table_base + table_offset + 8]
.copy_from_slice(&pfn.to_le_bytes());
table_offset += 8;
}
out[table_base + table_offset..table_base + table_offset + 8]
.copy_from_slice(&u64::MAX.to_le_bytes());
for (_, page_data) in &self.pages {
let compressed = xpress_compress_safe(page_data);
let compressed_size_field = (compressed.len() * 4 - 1) as u32;
let num_pages_minus_1: u8 = 0;
let mut block = Vec::new();
block.extend_from_slice(&XPRESS_SIG);
block.push(num_pages_minus_1);
block.push(compressed_size_field as u8);
block.push((compressed_size_field >> 8) as u8);
block.push((compressed_size_field >> 16) as u8);
block.resize(BLOCK_HEADER_SIZE, 0);
block.extend_from_slice(&compressed);
out.extend_from_slice(&block);
}
out
}
}
pub struct KdumpBuilder {
pages: Vec<(u64, Vec<u8>)>,
compression: u32,
block_size: u32,
}
impl Default for KdumpBuilder {
fn default() -> Self {
Self {
pages: Vec::new(),
compression: 0x04, block_size: 4096,
}
}
}
impl KdumpBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn block_size(mut self, bs: u32) -> Self {
self.block_size = bs;
self
}
pub fn compression(mut self, flags: u32) -> Self {
self.compression = flags;
self
}
pub fn add_page(mut self, pfn: u64, data: &[u8]) -> Self {
self.pages.push((pfn, data.to_vec()));
self
}
pub fn build(self) -> Vec<u8> {
let bs = self.block_size as usize;
let max_pfn = self
.pages
.iter()
.map(|(pfn, _)| *pfn)
.max()
.map_or(0, |p| p + 1);
let bitmap_bits = max_pfn as usize;
let bitmap_bytes_raw = bitmap_bits.div_ceil(8);
let bitmap_blocks = bitmap_bytes_raw.div_ceil(bs);
let bitmap_bytes = bitmap_blocks * bs;
let mut bitmap = vec![0u8; bitmap_bytes];
for (pfn, _) in &self.pages {
let pfn = *pfn as usize;
if pfn / 8 < bitmap.len() {
bitmap[pfn / 8] |= 1 << (pfn % 8);
}
}
let mut compressed_pages: Vec<(u32, Vec<u8>)> = Vec::new(); for (_, page_data) in &self.pages {
let (flags, compressed) = self.compress_page(page_data);
compressed_pages.push((flags, compressed));
}
let desc_start_block = 2 + 2 * bitmap_blocks;
let desc_start = desc_start_block * bs;
let mut indexed_pages: Vec<(usize, u64)> = self
.pages
.iter()
.enumerate()
.map(|(i, (pfn, _))| (i, *pfn))
.collect();
indexed_pages.sort_by_key(|(_, pfn)| *pfn);
let num_descs = indexed_pages.len();
let descs_raw_size = num_descs * 24;
let descs_padded = descs_raw_size.div_ceil(bs) * bs;
let data_start = desc_start + descs_padded;
let mut data_offsets: Vec<usize> = Vec::new();
let mut cur_offset = data_start;
for (orig_idx, _) in &indexed_pages {
data_offsets.push(cur_offset);
cur_offset += compressed_pages[*orig_idx].1.len();
}
let total_size = cur_offset;
let mut out = vec![0u8; total_size];
out[0x00..0x08].copy_from_slice(b"KDUMP ");
out[0x08..0x0C].copy_from_slice(&6i32.to_le_bytes());
let fields_off = (0x0C + 390 + 3) & !3; #[allow(clippy::cast_possible_wrap)]
let block_size_i32 = self.block_size as i32;
out[fields_off..fields_off + 4].copy_from_slice(&block_size_i32.to_le_bytes());
out[fields_off + 4..fields_off + 8].copy_from_slice(&1i32.to_le_bytes());
out[fields_off + 8..fields_off + 12].copy_from_slice(&(bitmap_blocks as u32).to_le_bytes());
out[fields_off + 12..fields_off + 16].copy_from_slice(&(max_pfn as u32).to_le_bytes());
let bm1_start = 2 * bs;
out[bm1_start..bm1_start + bitmap.len()].copy_from_slice(&bitmap);
let bm2_start = (2 + bitmap_blocks) * bs;
out[bm2_start..bm2_start + bitmap.len()].copy_from_slice(&bitmap);
for (desc_idx, (orig_idx, _)) in indexed_pages.iter().enumerate() {
let d_off = desc_start + desc_idx * 24;
let (flags, ref compressed) = compressed_pages[*orig_idx];
out[d_off..d_off + 8].copy_from_slice(
&{
#[allow(clippy::cast_possible_wrap)]
let offset_i64 = data_offsets[desc_idx] as i64;
offset_i64
}
.to_le_bytes(),
);
out[d_off + 8..d_off + 12].copy_from_slice(&(compressed.len() as u32).to_le_bytes());
out[d_off + 12..d_off + 16].copy_from_slice(&flags.to_le_bytes());
}
for (desc_idx, (orig_idx, _)) in indexed_pages.iter().enumerate() {
let offset = data_offsets[desc_idx];
let data = &compressed_pages[*orig_idx].1;
out[offset..offset + data.len()].copy_from_slice(data);
}
out
}
fn compress_page(&self, data: &[u8]) -> (u32, Vec<u8>) {
match self.compression {
0x00 => {
(0x00, data.to_vec())
}
0x01 => {
use std::io::Write;
let mut encoder =
flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
encoder.write_all(data).expect("zlib compress write");
let compressed = encoder.finish().expect("zlib compress finish");
(0x01, compressed)
}
0x04 => {
let mut encoder = snap::raw::Encoder::new();
let compressed = encoder.compress_vec(data).expect("snappy compress");
(0x04, compressed)
}
0x20 => {
let compressed = Self::zstd_compress_minimal(data);
(0x20, compressed)
}
_ => (0x00, data.to_vec()),
}
}
fn zstd_compress_minimal(data: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&0xFD2FB528u32.to_le_bytes());
out.push(0x00);
out.push(0x00);
let block_header: u32 = 1 | ((data.len() as u32) << 3);
let bh_bytes = block_header.to_le_bytes();
out.extend_from_slice(&bh_bytes[..3]);
out.extend_from_slice(data);
out
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::avml::AvmlProvider;
use crate::elf_core::ElfCoreProvider;
use crate::lime::LimeProvider;
use crate::PhysicalMemoryProvider;
#[test]
fn lime_builder_roundtrip() {
let data = vec![0xAB; 64];
let bytes = LimeBuilder::new().add_range(0x1000, &data).build();
let provider = LimeProvider::from_bytes(&bytes).unwrap();
assert_eq!(provider.ranges().len(), 1);
assert_eq!(provider.ranges()[0].start, 0x1000);
assert_eq!(provider.ranges()[0].end, 0x1000 + 64);
let mut buf = vec![0u8; 64];
let n = provider.read_phys(0x1000, &mut buf).unwrap();
assert_eq!(n, 64);
assert!(buf.iter().all(|&b| b == 0xAB));
}
#[test]
fn lime_builder_two_ranges() {
let bytes = LimeBuilder::new()
.add_range(0x0000, &[0x11; 32])
.add_range(0x8000, &[0x22; 48])
.build();
let provider = LimeProvider::from_bytes(&bytes).unwrap();
assert_eq!(provider.ranges().len(), 2);
assert_eq!(provider.total_size(), 32 + 48);
let mut buf = [0u8; 4];
let n = provider.read_phys(0x0000, &mut buf).unwrap();
assert_eq!(n, 4);
assert_eq!(buf, [0x11; 4]);
let n = provider.read_phys(0x8000, &mut buf).unwrap();
assert_eq!(n, 4);
assert_eq!(buf, [0x22; 4]);
}
#[test]
fn lime_builder_empty_produces_parseable_bytes() {
let bytes = LimeBuilder::new().build();
let provider = LimeProvider::from_bytes(&bytes).unwrap();
assert_eq!(provider.ranges().len(), 0);
assert_eq!(provider.total_size(), 0);
}
#[test]
fn avml_builder_roundtrip() {
let data = vec![0xCD; 128];
let bytes = AvmlBuilder::new().add_range(0x2000, &data).build();
let provider = AvmlProvider::from_bytes(&bytes).unwrap();
assert_eq!(provider.ranges().len(), 1);
assert_eq!(provider.ranges()[0].start, 0x2000);
assert_eq!(provider.ranges()[0].end, 0x2000 + 128);
let mut buf = vec![0u8; 128];
let n = provider.read_phys(0x2000, &mut buf).unwrap();
assert_eq!(n, 128);
assert!(buf.iter().all(|&b| b == 0xCD));
}
#[test]
fn avml_builder_two_ranges() {
let bytes = AvmlBuilder::new()
.add_range(0x0000, &[0xAA; 64])
.add_range(0x4000, &[0xBB; 96])
.build();
let provider = AvmlProvider::from_bytes(&bytes).unwrap();
assert_eq!(provider.ranges().len(), 2);
assert_eq!(provider.total_size(), 64 + 96);
let mut buf = [0u8; 4];
let n = provider.read_phys(0x0000, &mut buf).unwrap();
assert_eq!(n, 4);
assert_eq!(buf, [0xAA; 4]);
let n = provider.read_phys(0x4000, &mut buf).unwrap();
assert_eq!(n, 4);
assert_eq!(buf, [0xBB; 4]);
}
#[test]
fn elf_builder_roundtrip() {
let data = vec![0xEF; 256];
let bytes = ElfCoreBuilder::new().add_segment(0x3000, &data).build();
let provider = ElfCoreProvider::from_bytes(bytes).unwrap();
assert_eq!(provider.ranges().len(), 1);
assert_eq!(provider.ranges()[0].start, 0x3000);
assert_eq!(provider.ranges()[0].end, 0x3000 + 256);
let mut buf = vec![0u8; 256];
let n = provider.read_phys(0x3000, &mut buf).unwrap();
assert_eq!(n, 256);
assert!(buf.iter().all(|&b| b == 0xEF));
}
#[test]
fn elf_builder_two_segments() {
let bytes = ElfCoreBuilder::new()
.add_segment(0x0000, &[0x55; 512])
.add_segment(0x1000_0000, &[0x66; 128])
.build();
let provider = ElfCoreProvider::from_bytes(bytes).unwrap();
assert_eq!(provider.ranges().len(), 2);
assert_eq!(provider.total_size(), 512 + 128);
let mut buf = [0u8; 4];
let n = provider.read_phys(0x0000, &mut buf).unwrap();
assert_eq!(n, 4);
assert_eq!(buf, [0x55; 4]);
let n = provider.read_phys(0x1000_0000, &mut buf).unwrap();
assert_eq!(n, 4);
assert_eq!(buf, [0x66; 4]);
}
}