symtool_backend/
patch.rs

1//! Describe patches to an object.
2
3use crate::error::{Error, Result};
4use goblin::container::Ctx;
5use scroll::ctx::{SizeWith, TryIntoCtx};
6
7/// The location of a set of bytes in an object.
8#[derive(Debug)]
9pub(crate) struct Location {
10    /// The byte offset into the object
11    pub offset: usize,
12
13    /// The number of bytes
14    pub size: usize,
15
16    /// Contextual information containing endianness and object type
17    pub ctx: Ctx,
18}
19
20/// A value rooted to a location in an object.
21#[derive(Debug)]
22pub struct Rooted<T> {
23    pub value: T,
24    location: Location,
25}
26
27impl<T> Rooted<T> {
28    pub(crate) fn new(location: Location, value: T) -> Self {
29        Self { value, location }
30    }
31
32    /// Construct a patch that replaces this rooted value.
33    pub fn patch_with<U>(&self, value: U) -> Result<Patch>
34    where
35        U: TryIntoCtx<Ctx, [u8], Error = goblin::error::Error> + SizeWith<Ctx>,
36    {
37        Patch::from_ctx(&self.location, value)
38    }
39
40    /// Construct a patch that replaces this rooted value with specific bytes.
41    pub fn patch_with_bytes(&self, value: &[u8]) -> Result<Patch> {
42        Patch::from_bytes(&self.location, value)
43    }
44}
45
46impl<T> std::ops::Deref for Rooted<T> {
47    type Target = T;
48
49    fn deref(&self) -> &Self::Target {
50        &self.value
51    }
52}
53
54/// Represents a patch to an object.
55#[derive(Debug)]
56pub struct Patch {
57    offset: usize,
58    data: Vec<u8>,
59}
60
61impl Patch {
62    fn from_ctx<T>(location: &Location, data: T) -> Result<Self>
63    where
64        T: TryIntoCtx<Ctx, [u8], Error = goblin::error::Error> + SizeWith<Ctx>,
65    {
66        let size = T::size_with(&location.ctx);
67        if size > location.size {
68            return Err(Error::PatchTooBig);
69        }
70        let mut buf = vec![0u8; size];
71        data.try_into_ctx(&mut buf, location.ctx)?;
72        Ok(Self {
73            offset: location.offset,
74            data: buf,
75        })
76    }
77
78    fn from_bytes(location: &Location, data: &[u8]) -> Result<Self> {
79        if data.len() > location.size {
80            return Err(Error::PatchTooBig);
81        }
82        Ok(Self {
83            offset: location.offset,
84            data: data.to_vec(),
85        })
86    }
87
88    /// Apply the patch to the bytes of an object.
89    pub fn apply(&self, data: &mut [u8]) {
90        data[self.offset..(self.offset + self.data.len())].clone_from_slice(&self.data);
91    }
92}