Skip to main content

riscv_etrace/binary/
basic.rs

1// Copyright (C) 2025, 2026 FZI Forschungszentrum Informatik
2// SPDX-License-Identifier: Apache-2.0
3//! Basic [`Binary`]s and adapters
4
5use crate::instruction::{Instruction, decode, info};
6
7use super::Binary;
8use super::error;
9
10/// [`Binary`] adapter for an [`FnMut`]
11///
12/// This forwards calls to [`Binary::get_insn`] to the wrapped [`FnMut`].
13#[derive(Copy, Clone, Default, Debug, PartialEq)]
14pub struct Func<F: FnMut(u64) -> Result<Instruction<I>, E>, I: info::Info, E> {
15    func: F,
16    phantom: core::marker::PhantomData<(I, E)>,
17}
18
19impl<F: FnMut(u64) -> Result<Instruction<I>, E>, I: info::Info, E> Func<F, I, E> {
20    /// Create a new [`Binary`] from an [`FnMut`]
21    fn new(func: F) -> Self {
22        Self {
23            func,
24            phantom: Default::default(),
25        }
26    }
27}
28
29impl<F: FnMut(u64) -> Result<Instruction<I>, E>, I: info::Info, E> Binary<I> for Func<F, I, E> {
30    type Error = E;
31
32    fn get_insn(&mut self, address: u64) -> Result<Instruction<I>, Self::Error> {
33        (self.func)(address)
34    }
35}
36
37/// Create a [`Func`] [`Binary`] from an [`FnMut`]
38pub fn from_fn<F, I, E>(func: F) -> Func<F, I, E>
39where
40    F: FnMut(u64) -> Result<Instruction<I>, E>,
41    I: info::Info,
42{
43    Func::new(func)
44}
45
46/// [`Binary`] consisting of a single segment of encoded [`Instruction`]s
47///
48/// This [`Binary`] serves a single buffer as a code segment starting from
49/// address `0`.
50///
51/// # Example
52///
53/// The following example builds a segment at a specifig offset:
54///
55/// ```
56/// use riscv_etrace::binary::{self, Adaptable, Binary};
57/// use riscv_etrace::instruction::{self, base};
58///
59/// let bootrom = b"\x97\x02\x00\x00\x93\x85\x02\x02\x73\x25\x40\xf1\x83\xb2\x82\x01\x67\x80\x02\x00";
60/// let mut bootrom = binary::from_segment(bootrom, base::Set::Rv64I)
61///     .with_offset(0x1000);
62/// assert_eq!(
63///     bootrom.get_insn(0x1010),
64///     Ok(instruction::Kind::new_jalr(0, 5, 0).into()),
65/// );
66/// ```
67#[derive(Copy, Clone, Debug, PartialEq)]
68pub struct Segment<T: AsRef<[u8]>, B> {
69    data: T,
70    base: B,
71}
72
73impl<T: AsRef<[u8]>, B> Segment<T, B> {
74    /// Create a new [`Binary`] for code of a given instruction base set
75    pub fn new(data: T, base: B) -> Self {
76        Self { data, base }
77    }
78}
79
80impl<T: AsRef<[u8]>, B: decode::Decode<I>, I: info::Info> Binary<I> for Segment<T, B> {
81    type Error = error::SegmentError;
82
83    fn get_insn(&mut self, address: u64) -> Result<Instruction<I>, Self::Error> {
84        let offset = address.try_into().map_err(Self::Error::ExceededHostUSize)?;
85        let insn_data = self
86            .data
87            .as_ref()
88            .split_at_checked(offset)
89            .map(|(_, d)| d)
90            .filter(|d| !d.is_empty())
91            .ok_or(Self::Error::AddressNotCovered)?;
92        Instruction::extract(insn_data, &self.base)
93            .map(|(i, _)| i)
94            .ok_or(Self::Error::InvalidInstruction)
95    }
96}
97
98/// Create a new [`Binary`] for a segment of (raw) code
99pub fn from_segment<T: AsRef<[u8]>, B>(data: T, base: B) -> Segment<T, B> {
100    Segment::new(data, base)
101}
102
103/// [`Binary`] defined by a set of addresses-[`Instruction`] pairs
104///
105/// This [`Binary`] is backed by a slice of addresses-[`Instruction`] pairs
106/// specifying the presence of an [`Instruction`] at the specified address. The
107/// [`Binary`] is meant for small, fixed code sequences such as bootroms.
108#[derive(Copy, Clone, Default, Debug, PartialEq)]
109pub struct SimpleMap<T: AsRef<[(u64, Instruction<I>)]>, I: info::Info> {
110    inner: T,
111    phantom: core::marker::PhantomData<I>,
112}
113
114impl<T: AsRef<[(u64, Instruction<I>)]>, I: info::Info> SimpleMap<T, I> {
115    /// Create a new [`Binary`], potentially from a different type of container
116    ///
117    /// Prepares the slice held by the given container, then converts this to
118    /// the target type and returns the [`Binary`] contructed from that. This
119    /// allows creating a [`Binary`] operating on an [`Arc`][alloc::sync::Arc]
120    /// from containers that allow mutation of the slice such as
121    /// [`Box`][alloc::boxed::Box].
122    pub fn new<J>(mut inner: J) -> Self
123    where
124        T: From<J>,
125        J: AsMut<[(u64, Instruction<I>)]>,
126    {
127        inner.as_mut().sort_unstable_by_key(|(a, _)| *a);
128        Self {
129            inner: inner.into(),
130            phantom: Default::default(),
131        }
132    }
133
134    /// Create a [`Binary`] from a container holding a sorted slice
135    ///
136    /// Returns [`None`] if the slice is not sorted by address.
137    pub fn from_sorted(inner: T) -> Option<Self> {
138        inner
139            .as_ref()
140            .is_sorted_by_key(|(a, _)| *a)
141            .then_some(Self {
142                inner,
143                phantom: Default::default(),
144            })
145    }
146}
147
148impl<T, J, I> From<J> for SimpleMap<T, I>
149where
150    T: AsRef<[(u64, Instruction<I>)]> + From<J>,
151    J: AsMut<[(u64, Instruction<I>)]>,
152    I: info::Info,
153{
154    fn from(inner: J) -> Self {
155        Self::new(inner)
156    }
157}
158
159impl<T: AsRef<[(u64, Instruction<I>)]>, I: info::Info + Clone> Binary<I> for SimpleMap<T, I> {
160    type Error = error::NoInstruction;
161
162    fn get_insn(&mut self, address: u64) -> Result<Instruction<I>, Self::Error> {
163        let map = self.inner.as_ref();
164        map.binary_search_by_key(&address, |(a, _)| *a)
165            .map(|i| map[i].1.clone())
166            .map_err(|_| error::NoInstruction)
167    }
168}
169
170/// Create a [`Func`] [`Binary`] from some `AsRef<[(u64, Instruction)]>`
171///
172/// Returns `None` if the address-[`Instruction`] pairs are not sorted by
173/// address.
174pub fn from_sorted_map<T, I>(inner: T) -> Option<SimpleMap<T, I>>
175where
176    T: AsRef<[(u64, Instruction<I>)]>,
177    I: info::Info,
178{
179    SimpleMap::from_sorted(inner)
180}
181
182/// Create a [`Func`] [`Binary`] from some `AsMut<[(u64, Instruction)]>`
183pub fn from_map<T, J, I>(inner: J) -> SimpleMap<T, I>
184where
185    T: AsRef<[(u64, Instruction<I>)]> + From<J>,
186    J: AsMut<[(u64, Instruction<I>)]>,
187    I: info::Info,
188{
189    inner.into()
190}
191
192/// A [`Binary`] that does not contain any [`Instruction`]s
193#[derive(Copy, Clone, Default, Debug, PartialEq)]
194pub struct Empty;
195
196impl<I: info::Info> Binary<I> for Empty {
197    type Error = error::NoInstruction;
198
199    fn get_insn(&mut self, _: u64) -> Result<Instruction<I>, Self::Error> {
200        Err(error::NoInstruction)
201    }
202}