1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use std::fmt::Debug;
use crate::{AsmShmem, AsmShmemHeader};
#[repr(C)]
#[derive(Debug, Default)]
pub(crate) struct AsmRHHeader {
pub version: u64,
pub exit_code: u64,
pub shmem_allocated_size: u64,
pub steps: u64,
}
impl AsmShmemHeader for AsmRHHeader {
fn allocated_size(&self) -> u64 {
self.shmem_allocated_size
}
}
/// This struct represents the ROM histogram data collected from the assembly code execution.
#[repr(C)]
#[derive(Debug, Default)]
pub struct AsmRHData {
/// The number of steps executed.
pub steps: u64,
/// A vector containing the instruction count for each ROM.
pub inst_count: Vec<u64>,
}
impl AsmRHData {
/// Creates a new `AsmRHData` with the given number of steps and instruction count vector.
pub fn new(steps: u64, inst_count: Vec<u64>) -> Self {
AsmRHData { steps, inst_count }
}
}
impl AsmRHData {
/// Build an [`AsmRHData`] by reading the ROM histogram out of shared memory.
///
/// # Invariant (load-bearing)
/// `inst_count` is constructed with [`Vec::from_raw_parts`] pointing DIRECTLY
/// into the shared-memory mapping — it is NOT allocated by Rust's global
/// allocator. Dropping that `Vec` the normal way would make the allocator
/// free pages it never owned (undefined behavior / heap corruption).
///
/// The returned `AsmRHData` must therefore never be dropped normally:
/// `AsmRunnerRH::drop` (in `asm_rh_runner.rs`) `mem::forget`s it before the
/// mapping is torn down. These two sites are a matched pair — do not change
/// the `from_raw_parts` construction here without updating that `Drop`, and
/// vice versa.
pub(crate) fn from_shared_memory(asm_shared_memory: &AsmShmem<AsmRHHeader>) -> AsmRHData {
// SAFETY: `data_ptr` points into the live, read-only shared mapping owned by
// `asm_shared_memory`, which the caller keeps alive across this read. The
// header read and `Vec::from_raw_parts` stay in bounds — the `assert!` below
// rejects any `len` that would run past the mapped region. The returned `Vec`
// aliases the mapping and must never be freed by Rust's allocator; see the
// `# Invariant` above and `AsmRunnerRH::drop`.
unsafe {
let data_ptr = asm_shared_memory.data_ptr() as *mut u64;
// chunk data
let len = std::ptr::read(data_ptr) as usize;
assert!(
(len + 1) * 8
<= (asm_shared_memory.mapped_size() - std::mem::size_of::<AsmRHHeader>()),
"Data length {} exceeds allocated shared memory size",
len
);
let data_ptr = data_ptr.add(1);
let inst_count = Vec::from_raw_parts(data_ptr, len, len);
AsmRHData { steps: asm_shared_memory.map_header().steps, inst_count }
}
}
}