Skip to main content

minidump_writer/
mem_writer.rs

1use {
2    crate::{
3        minidump_format::{MDLocationDescriptor, MDRVA},
4        serializers::*,
5    },
6    scroll::ctx::{SizeWith, TryIntoCtx},
7};
8
9#[derive(Debug, thiserror::Error, serde::Serialize)]
10pub enum MemoryWriterError {
11    #[error("IO error when writing to DumpBuf")]
12    IOError(
13        #[from]
14        #[serde(serialize_with = "serialize_io_error")]
15        std::io::Error,
16    ),
17    #[error("Failed integer conversion")]
18    TryFromIntError(
19        #[from]
20        #[serde(skip)]
21        std::num::TryFromIntError,
22    ),
23    #[error("Failed to write to buffer")]
24    Scroll(
25        #[from]
26        #[serde(serialize_with = "serialize_scroll_error")]
27        scroll::Error,
28    ),
29}
30
31type WriteResult<T> = std::result::Result<T, MemoryWriterError>;
32
33macro_rules! size {
34    ($t:ty) => {
35        <$t>::size_with(&scroll::Endian::Little)
36    };
37}
38
39pub struct Buffer {
40    inner: Vec<u8>,
41}
42
43impl Buffer {
44    pub fn with_capacity(cap: usize) -> Self {
45        Self {
46            inner: Vec::with_capacity(cap),
47        }
48    }
49
50    #[inline]
51    pub fn position(&self) -> u64 {
52        self.inner.len() as u64
53    }
54
55    #[inline]
56    #[must_use]
57    fn reserve(&mut self, len: usize) -> usize {
58        let mark = self.inner.len();
59        self.inner.resize(self.inner.len() + len, 0);
60        mark
61    }
62
63    #[inline]
64    fn write<N, E>(&mut self, val: N) -> Result<usize, E>
65    where
66        N: TryIntoCtx<scroll::Endian, Error = E> + SizeWith<scroll::Endian>,
67        E: From<scroll::Error>,
68    {
69        self.write_at(self.inner.len(), val)
70    }
71
72    fn write_at<N, E>(&mut self, offset: usize, val: N) -> Result<usize, E>
73    where
74        N: TryIntoCtx<scroll::Endian, Error = E> + SizeWith<scroll::Endian>,
75        E: From<scroll::Error>,
76    {
77        let to_write = size!(N);
78        let remainder = self.inner.len() - offset;
79        if remainder < to_write {
80            self.inner
81                .resize(self.inner.len() + to_write - remainder, 0);
82        }
83
84        let dst = &mut self.inner[offset..offset + to_write];
85        val.try_into_ctx(dst, scroll::Endian::Little)
86    }
87
88    #[inline]
89    pub fn write_all(&mut self, buffer: &[u8]) {
90        self.inner.extend_from_slice(buffer);
91    }
92}
93
94impl From<Buffer> for Vec<u8> {
95    fn from(b: Buffer) -> Self {
96        b.inner
97    }
98}
99
100impl std::ops::Deref for Buffer {
101    type Target = [u8];
102
103    fn deref(&self) -> &Self::Target {
104        &self.inner
105    }
106}
107
108#[derive(Debug)]
109pub struct MemoryWriter<T> {
110    pub position: MDRVA,
111    pub size: usize,
112    phantom: std::marker::PhantomData<T>,
113}
114
115impl<T> MemoryWriter<T>
116where
117    T: TryIntoCtx<scroll::Endian, Error = scroll::Error> + SizeWith<scroll::Endian>,
118{
119    /// Create a slot for a type T in the buffer, we can fill right now with real values.
120    pub fn alloc_with_val(buffer: &mut Buffer, val: T) -> WriteResult<Self> {
121        // Mark the position as we may overwrite later
122        let position = buffer.position();
123        let size = buffer.write(val)?;
124
125        Ok(Self {
126            position: position as u32,
127            size,
128            phantom: std::marker::PhantomData,
129        })
130    }
131
132    /// Create a slot for a type T in the buffer, we can fill later with real values.
133    pub fn alloc(buffer: &mut Buffer) -> WriteResult<Self> {
134        let size = size!(T);
135        let position = buffer.reserve(size) as u32;
136
137        Ok(Self {
138            position,
139            size,
140            phantom: std::marker::PhantomData,
141        })
142    }
143
144    /// Write actual values in the buffer-slot we got during `alloc()`
145    #[inline]
146    pub fn set_value(&mut self, buffer: &mut Buffer, val: T) -> WriteResult<()> {
147        Ok(buffer.write_at(self.position as usize, val).map(|_sz| ())?)
148    }
149
150    #[inline]
151    pub fn location(&self) -> MDLocationDescriptor {
152        MDLocationDescriptor {
153            data_size: size!(T) as u32,
154            rva: self.position,
155        }
156    }
157}
158
159#[derive(Debug)]
160pub struct MemoryArrayWriter<T> {
161    pub position: MDRVA,
162    array_size: usize,
163    phantom: std::marker::PhantomData<T>,
164}
165
166#[cfg(any(target_os = "linux", target_os = "android"))]
167impl MemoryArrayWriter<u8> {
168    #[inline]
169    pub fn write_bytes(buffer: &mut Buffer, slice: &[u8]) -> Self {
170        let position = buffer.position();
171        buffer.write_all(slice);
172
173        Self {
174            position: position as u32,
175            array_size: slice.len(),
176            phantom: std::marker::PhantomData,
177        }
178    }
179}
180
181impl<T> MemoryArrayWriter<T>
182where
183    T: TryIntoCtx<scroll::Endian, Error = scroll::Error> + SizeWith<scroll::Endian> + Copy,
184{
185    pub fn alloc_from_array(buffer: &mut Buffer, array: &[T]) -> WriteResult<Self> {
186        let array_size = array.len();
187        let position = buffer.reserve(array_size * size!(T));
188
189        for (idx, val) in array.iter().enumerate() {
190            buffer.write_at(position + idx * size!(T), *val)?;
191        }
192
193        Ok(Self {
194            position: position as u32,
195            array_size,
196            phantom: std::marker::PhantomData,
197        })
198    }
199}
200
201impl<T> MemoryArrayWriter<T>
202where
203    T: TryIntoCtx<scroll::Endian, Error = scroll::Error> + SizeWith<scroll::Endian>,
204{
205    /// Create a slot for a type T in the buffer, we can fill in the values in one go.
206    pub fn alloc_from_iter<I>(
207        buffer: &mut Buffer,
208        iter: impl IntoIterator<Item = T, IntoIter = I>,
209    ) -> WriteResult<Self>
210    where
211        I: std::iter::ExactSizeIterator<Item = T>,
212    {
213        let iter = iter.into_iter();
214        let array_size = iter.len();
215        let size = size!(T);
216        let position = buffer.reserve(array_size * size);
217
218        for (idx, val) in iter.enumerate() {
219            buffer.write_at(position + idx * size, val)?;
220        }
221
222        Ok(Self {
223            position: position as u32,
224            array_size,
225            phantom: std::marker::PhantomData,
226        })
227    }
228
229    /// Create a slot for a type T in the buffer, we can fill later with real values.
230    /// This function fills it with `Default::default()`, which is less performant than
231    /// using uninitialized memory, but safe.
232    pub fn alloc_array(buffer: &mut Buffer, array_size: usize) -> WriteResult<Self> {
233        let position = buffer.reserve(array_size * size!(T));
234
235        Ok(Self {
236            position: position as u32,
237            array_size,
238            phantom: std::marker::PhantomData,
239        })
240    }
241
242    /// Write actual values in the buffer-slot we got during `alloc()`
243    #[inline]
244    pub fn set_value_at(&mut self, buffer: &mut Buffer, val: T, index: usize) -> WriteResult<()> {
245        Ok(buffer
246            .write_at(self.position as usize + size!(T) * index, val)
247            .map(|_sz| ())?)
248    }
249
250    #[inline]
251    pub fn location(&self) -> MDLocationDescriptor {
252        MDLocationDescriptor {
253            data_size: (self.array_size * size!(T)) as u32,
254            rva: self.position,
255        }
256    }
257
258    #[inline]
259    pub fn location_of_index(&self, idx: usize) -> MDLocationDescriptor {
260        MDLocationDescriptor {
261            data_size: size!(T) as u32,
262            rva: self.position + (size!(T) * idx) as u32,
263        }
264    }
265}
266
267pub fn write_string_to_location(
268    buffer: &mut Buffer,
269    text: &str,
270) -> WriteResult<MDLocationDescriptor> {
271    let letters: Vec<u16> = text.encode_utf16().collect();
272
273    // First write size of the string (x letters in u16, times the size of u16)
274    let text_header = MemoryWriter::<u32>::alloc_with_val(
275        buffer,
276        (letters.len() * std::mem::size_of::<u16>()).try_into()?,
277    )?;
278
279    // Then write utf-16 letters after that
280    let mut text_section = MemoryArrayWriter::<u16>::alloc_array(buffer, letters.len())?;
281    for (index, letter) in letters.iter().enumerate() {
282        text_section.set_value_at(buffer, *letter, index)?;
283    }
284
285    let mut location = text_header.location();
286    location.data_size += text_section.location().data_size;
287
288    Ok(location)
289}