minidump_synth/
lib.rs

1// Copyright 2016 Ted Mielczarek. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3
4//! Synthetic Minidumps for Testing
5//!
6//! This is a hacky mess that intentionally doesn't use minidump-common's layouts
7//! so that we can catch incorrect changes to minidump-common itself. It exists
8//! primarily as an internal dev-dependency of rust-minidump, but is published
9//! for the sake of satisfying cargo-publish.
10//!
11//! Basic usage is to  [SynthMinidump][], use its methods to build up the binary,
12//! and then `finish()` to write the binary to a buffer. Then you can either write
13//! that to disk as an actual minidump file or feed it directly to the minidump
14//! or minidump-processor crate.
15
16// Some test_assembler types do not have Debug, so be a bit more lenient here.
17#![allow(missing_debug_implementations)]
18
19use minidump_common::format as md;
20use scroll::ctx::SizeWith;
21use scroll::LE;
22use std::marker::PhantomData;
23use std::mem;
24use test_assembler::*;
25
26/// A writer of synthetic minidumps.
27pub struct SynthMinidump {
28    /// The `Section` containing the minidump contents.
29    section: Section,
30    /// The minidump flags, for the header.
31    flags: Label,
32    /// The number of streams.
33    stream_count: u32,
34    /// The number of streams, as a label for the header.
35    stream_count_label: Label,
36    /// The directory's file offset, for the header.
37    stream_directory_rva: Label,
38    /// The contents of the stream directory.
39    stream_directory: Section,
40    /// System info (cpu arch, os, etc.)
41    system_info: Option<SystemInfo>,
42    /// High level crash info (error code, crash address, assertion, ...)
43    exception: Option<Exception>,
44    /// List of modules in this minidump.
45    module_list: Option<ListStream<Module>>,
46    /// List of unloaded modules in this minidump.
47    unloaded_module_list: Option<ExListStream<UnloadedModule>>,
48    /// List of threads in this minidump.
49    thread_list: Option<ListStream<Thread>>,
50    /// List of thread names in this minidump.
51    thread_names_list: Option<ListStream<ThreadName>>,
52    /// List of memory regions in this minidump.
53    memory_list: Option<ListStream<Section>>,
54    /// List of memory regions stored in `Memory64List` stream in this minidump.
55    memory64_list: Option<Memory64ListStream>,
56    /// List of extra info about memory regions in this minidump.
57    memory_info_list: Option<ExListStream<MemoryInfo>>,
58    /// Crashpad extension containing annotations.
59    crashpad_info: Option<CrashpadInfo>,
60    /// /proc/self/maps string
61    linux_maps: Option<SimpleStream>,
62    /// /etc/lsb-release string
63    linux_lsb_release: Option<SimpleStream>,
64    /// /proc/cpuinfo string
65    linux_cpu_info: Option<SimpleStream>,
66    /// /proc/self/environ string
67    linux_environ: Option<SimpleStream>,
68    /// /proc/self/status string
69    linux_proc_status: Option<SimpleStream>,
70    /// /proc/self/limits string
71    linux_proc_limits: Option<SimpleStream>,
72    /// Soft errors JSON string
73    soft_errors: Option<SimpleStream>,
74    /// Continuous memory used by `Memory64List` stream
75    memory64_section: Option<Section>,
76    /// List of handles in this minidump.
77    handle_data_stream: Option<ExListStream<HandleDescriptor>>,
78}
79
80/// A block of data contained in a minidump.
81pub trait DumpSection {
82    /// A label representing this `DumpSection`'s offset in bytes from the start of the minidump.
83    fn file_offset(&self) -> Label;
84
85    /// A label representing this `DumpSection`'s size in bytes within the minidump.
86    fn file_size(&self) -> Label;
87}
88
89/// A list item with optional out-of-band data.
90///
91/// Items can be added to [`List`]. The main sections returned from [`ListItem::into_sections`] are
92/// stored in a compact list, followed by all out-of-band data in implementation-defined order.
93///
94/// For convenience, `ListItem` is implemented for every type that implements `Into<Section>`, so
95/// that it can be used directly for types that do not require out-of-band data. Prefer to implement
96/// `Into<Section>` unless out-of-band data is explicitly required.
97pub trait ListItem: DumpSection {
98    /// Returns a pair of sections for in-band and out-of-band data.
99    fn into_sections(self) -> (Section, Option<Section>);
100}
101
102impl<T> ListItem for T
103where
104    T: Into<Section> + DumpSection,
105{
106    fn into_sections(self) -> (Section, Option<Section>) {
107        (self.into(), None)
108    }
109}
110
111pub trait CiteLocation {
112    /// Append an `MINIDUMP_LOCATION_DESCRIPTOR` to `section` referring to this section.
113    fn cite_location_in(&self, section: Section) -> Section;
114}
115
116impl<T: DumpSection> CiteLocation for T {
117    fn cite_location_in(&self, section: Section) -> Section {
118        // An MINIDUMP_LOCATION_DESCRIPTOR is just a 32-bit size + 32-bit offset.
119        section.D32(self.file_size()).D32(self.file_offset())
120    }
121}
122
123impl CiteLocation for (Label, Label) {
124    fn cite_location_in(&self, section: Section) -> Section {
125        section.D32(&self.0).D32(&self.1)
126    }
127}
128
129impl<T: CiteLocation> CiteLocation for Option<T> {
130    fn cite_location_in(&self, section: Section) -> Section {
131        match *self {
132            Some(ref inner) => inner.cite_location_in(section),
133            None => section.D32(0).D32(0),
134        }
135    }
136}
137
138/// Additional methods to make working with `Section`s simpler
139pub trait SectionExtra {
140    /// A chainable version of `CiteLocation::cite_location_in`
141    fn cite_location<T: CiteLocation>(self, thing: &T) -> Self;
142    /// A chainable version of `Memory::cite_memory_in`
143    fn cite_memory(self, memory: &Memory) -> Self;
144}
145
146impl SectionExtra for Section {
147    fn cite_location<T: CiteLocation>(self, thing: &T) -> Self {
148        thing.cite_location_in(self)
149    }
150    fn cite_memory(self, memory: &Memory) -> Self {
151        memory.cite_memory_in(self)
152    }
153}
154
155/// A minidump stream.
156pub trait Stream: DumpSection + Into<Section> {
157    /// The stream type, used in the stream directory.
158    fn stream_type(&self) -> u32;
159    /// Append an `MDRawDirectory` referring to this stream to `section`.
160    fn cite_stream_in(&self, section: Section) -> Section {
161        section.D32(self.stream_type()).cite_location(self)
162    }
163}
164
165impl SynthMinidump {
166    /// Create a `SynthMinidump` with default endianness.
167    pub fn new() -> SynthMinidump {
168        SynthMinidump::with_endian(DEFAULT_ENDIAN)
169    }
170
171    /// Create a `SynthMinidump` with `endian` endianness.
172    pub fn with_endian(endian: Endian) -> SynthMinidump {
173        let flags = Label::new();
174        let stream_count_label = Label::new();
175        let stream_directory_rva = Label::new();
176        let section = Section::with_endian(endian)
177            .D32(md::MINIDUMP_SIGNATURE)
178            .D32(md::MINIDUMP_VERSION)
179            .D32(&stream_count_label)
180            .D32(&stream_directory_rva)
181            .D32(0)
182            .D32(1262805309) // date_time_stamp, arbitrary
183            .D64(&flags);
184        section.start().set_const(0);
185        assert_eq!(section.size(), mem::size_of::<md::MINIDUMP_HEADER>() as u64);
186        let memory64_section = Section::with_endian(endian);
187
188        SynthMinidump {
189            section,
190            flags,
191            stream_count: 0,
192            stream_count_label,
193            stream_directory_rva,
194            stream_directory: Section::with_endian(endian),
195            system_info: None,
196            exception: None,
197            module_list: Some(ListStream::new(
198                md::MINIDUMP_STREAM_TYPE::ModuleListStream,
199                endian,
200            )),
201            unloaded_module_list: Some(ExListStream::new(
202                md::MINIDUMP_STREAM_TYPE::UnloadedModuleListStream,
203                mem::size_of::<md::MINIDUMP_UNLOADED_MODULE>(),
204                endian,
205            )),
206            thread_list: Some(ListStream::new(
207                md::MINIDUMP_STREAM_TYPE::ThreadListStream,
208                endian,
209            )),
210            thread_names_list: Some(ListStream::new(
211                md::MINIDUMP_STREAM_TYPE::ThreadNamesStream,
212                endian,
213            )),
214            memory_list: Some(ListStream::new(
215                md::MINIDUMP_STREAM_TYPE::MemoryListStream,
216                endian,
217            )),
218            memory64_list: Some(Memory64ListStream::new(
219                endian,
220                &memory64_section.file_offset(),
221            )),
222            memory_info_list: Some(ExListStream::new(
223                md::MINIDUMP_STREAM_TYPE::MemoryInfoListStream,
224                mem::size_of::<md::MINIDUMP_MEMORY_INFO>(),
225                endian,
226            )),
227            linux_maps: None,
228            linux_lsb_release: None,
229            linux_environ: None,
230            linux_cpu_info: None,
231            linux_proc_status: None,
232            linux_proc_limits: None,
233            soft_errors: None,
234            crashpad_info: None,
235            memory64_section: Some(memory64_section),
236            handle_data_stream: Some(ExListStream::new_with_header_size(
237                md::MINIDUMP_STREAM_TYPE::HandleDataStream,
238                mem::size_of::<md::MINIDUMP_HANDLE_DATA_STREAM>(),
239                mem::size_of::<md::MINIDUMP_HANDLE_DESCRIPTOR>(),
240                endian,
241            )),
242        }
243    }
244
245    /// Set the minidump flags to `flags`.
246    pub fn flags(self, flags: u64) -> SynthMinidump {
247        self.flags.set_const(flags);
248        self
249    }
250
251    /// Append `section` to `self`, setting its location appropriately.
252    // Perhaps should have been called .add_section().
253    #[allow(clippy::should_implement_trait)]
254    pub fn add<T>(mut self, section: T) -> SynthMinidump
255    where
256        T: DumpSection + Into<Section>,
257    {
258        let offset = section.file_offset();
259        self.section = self.section.mark(&offset).append_section(section);
260        self
261    }
262
263    /// Add `module` to `self`, adding it to the module list stream as well.
264    pub fn add_module(mut self, module: Module) -> SynthMinidump {
265        self.module_list = self
266            .module_list
267            .take()
268            .map(|module_list| module_list.add(module));
269        self
270    }
271
272    /// Add `module` to `self`, adding it to the unloaded module list stream as well.
273    pub fn add_unloaded_module(mut self, module: UnloadedModule) -> SynthMinidump {
274        self.unloaded_module_list = self
275            .unloaded_module_list
276            .take()
277            .map(|module_list| module_list.add(module));
278        self
279    }
280
281    /// Add `memory` to `self`, adding it to the memory list stream as well.
282    pub fn add_memory(mut self, memory: Memory) -> SynthMinidump {
283        // The memory list contains `MINIDUMP_MEMORY_DESCRIPTOR`s, so create one here.
284        let descriptor = memory.cite_memory_in(Section::with_endian(self.section.endian));
285        // And append that descriptor to the memory list.
286        self.memory_list = self
287            .memory_list
288            .take()
289            .map(|memory_list| memory_list.add(descriptor));
290        // Add the memory region itself.
291        self.add(memory)
292    }
293
294    /// Add `memory` to `self`'s memory64 list
295    pub fn add_memory64(mut self, memory: Memory) -> SynthMinidump {
296        self.memory64_list = self
297            .memory64_list
298            .take()
299            .map(|memory64_list| memory64_list.add_memory(&memory));
300        self.memory64_section = self
301            .memory64_section
302            .take()
303            .map(|memory64_section| memory64_section.append_section(memory.section));
304        self
305    }
306
307    /// Add `info` to `self`, adding it to the memory info list stream as well.
308    pub fn add_memory_info(mut self, info: MemoryInfo) -> SynthMinidump {
309        self.memory_info_list = self
310            .memory_info_list
311            .take()
312            .map(|info_list| info_list.add(info));
313        self
314    }
315
316    /// Add `thread` to `self`, adding it to the thread list stream as well.
317    pub fn add_thread(mut self, thread: Thread) -> SynthMinidump {
318        self.thread_list = self
319            .thread_list
320            .take()
321            .map(|thread_list| thread_list.add(thread));
322        self
323    }
324
325    /// Add `thread_name` to `self`, adding it to the thread name stream as well.
326    pub fn add_thread_name(mut self, thread_name: ThreadName) -> SynthMinidump {
327        self.thread_names_list = self
328            .thread_names_list
329            .take()
330            .map(|thread_names_list| thread_names_list.add(thread_name));
331        self
332    }
333
334    /// Add `handle` to `self`, adding it to the handle data stream as well.
335    pub fn add_handle_descriptor(mut self, handle: HandleDescriptor) -> SynthMinidump {
336        self.handle_data_stream = self
337            .handle_data_stream
338            .take()
339            .map(|handle_data_stream| handle_data_stream.add(handle));
340        self
341    }
342
343    /// Add crashpad module and annotation extension information.
344    pub fn add_crashpad_info(mut self, crashpad_info: CrashpadInfo) -> Self {
345        self.crashpad_info = Some(crashpad_info);
346        self
347    }
348
349    /// Set the SystemInfo stream.
350    pub fn add_system_info(mut self, system_info: SystemInfo) -> Self {
351        self.system_info = Some(system_info);
352        self
353    }
354
355    /// Set the Exception stream.
356    pub fn add_exception(mut self, exception: Exception) -> Self {
357        self.exception = Some(exception);
358        self
359    }
360
361    /// Set the contents of the `LinuxMaps` stream.
362    pub fn set_linux_maps(mut self, maps: &[u8]) -> SynthMinidump {
363        self.linux_maps = Some(SimpleStream {
364            stream_type: md::MINIDUMP_STREAM_TYPE::LinuxMaps as u32,
365            section: Section::new().append_bytes(maps),
366        });
367        self
368    }
369
370    /// Set the contents of the `LinuxLsbRelease` stream.
371    pub fn set_linux_lsb_release(mut self, lsb: &[u8]) -> SynthMinidump {
372        self.linux_lsb_release = Some(SimpleStream {
373            stream_type: md::MINIDUMP_STREAM_TYPE::LinuxLsbRelease as u32,
374            section: Section::new().append_bytes(lsb),
375        });
376        self
377    }
378
379    /// Set the contents of the `LinuxProcStatus` stream.
380    pub fn set_linux_proc_status(mut self, proc_status: &[u8]) -> SynthMinidump {
381        self.linux_proc_status = Some(SimpleStream {
382            stream_type: md::MINIDUMP_STREAM_TYPE::LinuxProcStatus as u32,
383            section: Section::new().append_bytes(proc_status),
384        });
385        self
386    }
387
388    /// Set the contents of the `LinuxProcLimits` stream.
389    pub fn set_linux_proc_limits(mut self, proc_limits: &[u8]) -> SynthMinidump {
390        self.linux_proc_limits = Some(SimpleStream {
391            stream_type: md::MINIDUMP_STREAM_TYPE::MozLinuxLimits as u32,
392            section: Section::new().append_bytes(proc_limits),
393        });
394        self
395    }
396
397    /// Set the contents of the `MozSoftErrors` stream.
398    pub fn set_soft_errors(mut self, soft_errors: &str) -> SynthMinidump {
399        self.soft_errors = Some(SimpleStream {
400            stream_type: md::MINIDUMP_STREAM_TYPE::MozSoftErrors as u32,
401            section: Section::new().append_bytes(soft_errors.as_bytes()),
402        });
403        self
404    }
405
406    /// Set the contents of the `LinuxCpuInfo` stream.
407    pub fn set_linux_cpu_info(mut self, cpu_info: &[u8]) -> SynthMinidump {
408        self.linux_cpu_info = Some(SimpleStream {
409            stream_type: md::MINIDUMP_STREAM_TYPE::LinuxCpuInfo as u32,
410            section: Section::new().append_bytes(cpu_info),
411        });
412        self
413    }
414
415    /// Set the contents of the `LinuxEnviron` stream.
416    pub fn set_linux_environ(mut self, environ: &[u8]) -> SynthMinidump {
417        self.linux_environ = Some(SimpleStream {
418            stream_type: md::MINIDUMP_STREAM_TYPE::LinuxEnviron as u32,
419            section: Section::new().append_bytes(environ),
420        });
421        self
422    }
423
424    /// Append `stream` to `self`, setting its location appropriately and adding it to the stream directory.
425    pub fn add_stream<T: Stream>(mut self, stream: T) -> SynthMinidump {
426        self.stream_directory = stream.cite_stream_in(self.stream_directory);
427        self.stream_count += 1;
428        self.add(stream)
429    }
430
431    fn finish_list<T: ListItem>(self, list: Option<ListStream<T>>) -> SynthMinidump {
432        match list {
433            Some(l) => {
434                if !l.is_empty() {
435                    self.add_stream(l)
436                } else {
437                    self
438                }
439            }
440            None => self,
441        }
442    }
443
444    fn finish_ex_list<T: ListItem>(self, list: Option<ExListStream<T>>) -> SynthMinidump {
445        match list {
446            Some(l) => {
447                if !l.is_empty() {
448                    self.add_stream(l)
449                } else {
450                    self
451                }
452            }
453            None => self,
454        }
455    }
456
457    /// Finish generating the minidump and return the contents.
458    pub fn finish(mut self) -> Option<Vec<u8>> {
459        // Add module list stream if any modules were added.
460        let modules = self.module_list.take();
461        self = self.finish_list(modules);
462        // Add unloaded module list stream if any unloaded modules were added.
463        let unloaded_modules = self.unloaded_module_list.take();
464        self = self.finish_ex_list(unloaded_modules);
465        // Add memory list stream if any memory regions were added.
466        let memories = self.memory_list.take();
467        self = self.finish_list(memories);
468        // Add memory64 list stream if any memory regions were added.
469        if let Some(memories64) = self.memory64_list.take() {
470            if !memories64.is_empty() {
471                self = self.add_stream(memories64);
472            }
473        }
474        // Add memory info list stream if any memory infos were added.
475        let memory_infos = self.memory_info_list.take();
476        self = self.finish_ex_list(memory_infos);
477        // Add thread list stream if any threads were added.
478        let threads = self.thread_list.take();
479        self = self.finish_list(threads);
480        // Add thread names stream if any names were added.
481        let thread_names = self.thread_names_list.take();
482        self = self.finish_list(thread_names);
483        // Add crashpad info stream if any.
484        if let Some(crashpad_info) = self.crashpad_info.take() {
485            self = self.add_stream(crashpad_info);
486        }
487        if let Some(stream) = self.system_info.take() {
488            self = self.add_stream(stream);
489        }
490        if let Some(stream) = self.exception.take() {
491            self = self.add_stream(stream);
492        }
493        if let Some(stream) = self.linux_maps.take() {
494            self = self.add_stream(stream);
495        }
496        if let Some(stream) = self.linux_lsb_release.take() {
497            self = self.add_stream(stream);
498        }
499        if let Some(stream) = self.linux_cpu_info.take() {
500            self = self.add_stream(stream);
501        }
502        if let Some(stream) = self.linux_proc_status.take() {
503            self = self.add_stream(stream);
504        }
505        if let Some(stream) = self.linux_proc_limits.take() {
506            self = self.add_stream(stream);
507        }
508        if let Some(stream) = self.soft_errors.take() {
509            self = self.add_stream(stream);
510        }
511        if let Some(stream) = self.linux_environ.take() {
512            self = self.add_stream(stream);
513        }
514        if let Some(memory64_section) = self.memory64_section.take() {
515            self = self.add(memory64_section);
516        }
517        // Add the handle data stream if any handle descriptors were added.
518        let handle_data = self.handle_data_stream.take();
519        self = self.finish_ex_list(handle_data);
520
521        let SynthMinidump {
522            section,
523            flags,
524            stream_count,
525            stream_count_label,
526            stream_directory_rva,
527            stream_directory,
528            ..
529        } = self;
530        if flags.value().is_none() {
531            flags.set_const(0);
532        }
533        // Create the stream directory.
534        stream_count_label.set_const(stream_count as u64);
535        section
536            .mark(&stream_directory_rva)
537            .append_section(stream_directory)
538            .get_contents()
539    }
540}
541
542impl Default for SynthMinidump {
543    fn default() -> Self {
544        Self::new()
545    }
546}
547
548impl DumpSection for Section {
549    fn file_offset(&self) -> Label {
550        self.start()
551    }
552
553    fn file_size(&self) -> Label {
554        self.final_size()
555    }
556}
557
558macro_rules! impl_dumpsection {
559    ( $x:ty ) => {
560        impl DumpSection for $x {
561            fn file_offset(&self) -> Label {
562                self.section.file_offset()
563            }
564            fn file_size(&self) -> Label {
565                self.section.file_size()
566            }
567        }
568    };
569}
570
571/// A stream of arbitrary data.
572pub struct SimpleStream {
573    /// The stream type.
574    pub stream_type: u32,
575    /// The stream's contents.
576    pub section: Section,
577}
578
579impl From<SimpleStream> for Section {
580    fn from(stream: SimpleStream) -> Self {
581        stream.section
582    }
583}
584
585impl_dumpsection!(SimpleStream);
586
587impl Stream for SimpleStream {
588    fn stream_type(&self) -> u32 {
589        self.stream_type
590    }
591}
592
593/// A stream containing a list of dump entries.
594pub struct List<T: ListItem> {
595    /// The stream's contents.
596    section: Section,
597    /// The number of entries.
598    count: u32,
599    /// The number of entries, as a `Label`.
600    count_label: Label,
601    /// Out-of-band data referenced by this stream's contents.
602    out_of_band: Section,
603    _type: PhantomData<T>,
604}
605
606impl<T: ListItem> List<T> {
607    pub fn new(endian: Endian) -> Self {
608        let count_label = Label::new();
609        List {
610            section: Section::with_endian(endian).D32(&count_label),
611            count_label,
612            count: 0,
613            out_of_band: Section::with_endian(endian),
614            _type: PhantomData,
615        }
616    }
617
618    // Possibly name this .add_section().
619    #[allow(clippy::should_implement_trait)]
620    pub fn add(mut self, entry: T) -> Self {
621        self.count += 1;
622
623        let (section, out_of_band_opt) = entry.into_sections();
624
625        self.section = self
626            .section
627            .mark(&section.file_offset())
628            .append_section(section);
629
630        if let Some(out_of_band) = out_of_band_opt {
631            self.out_of_band = self
632                .out_of_band
633                .mark(&out_of_band.file_offset())
634                .append_section(out_of_band);
635        }
636
637        self
638    }
639
640    pub fn is_empty(&self) -> bool {
641        self.count == 0
642    }
643}
644
645impl<T: ListItem> From<List<T>> for Section {
646    fn from(list: List<T>) -> Self {
647        // Finalize the entry count.
648        list.count_label.set_const(list.count as u64);
649
650        // Serialize all (transitive) out-of-band data after the dense list of entry records.
651        list.section
652            .mark(&list.out_of_band.file_offset())
653            .append_section(list.out_of_band)
654    }
655}
656
657impl<T: ListItem> DumpSection for List<T> {
658    fn file_offset(&self) -> Label {
659        self.section.file_offset()
660    }
661
662    fn file_size(&self) -> Label {
663        self.section.file_size()
664    }
665}
666
667pub struct ListStream<T: ListItem> {
668    /// The stream type.
669    stream_type: u32,
670    /// The list containing items.
671    list: List<T>,
672}
673
674impl<T: ListItem> ListStream<T> {
675    pub fn new<S: Into<u32>>(stream_type: S, endian: Endian) -> Self {
676        Self {
677            stream_type: stream_type.into(),
678            list: List::new(endian),
679        }
680    }
681
682    #[allow(clippy::should_implement_trait)]
683    pub fn add(mut self, entry: T) -> Self {
684        self.list = self.list.add(entry);
685        self
686    }
687
688    pub fn is_empty(&self) -> bool {
689        self.list.is_empty()
690    }
691}
692
693impl<T: ListItem> From<ListStream<T>> for Section {
694    fn from(stream: ListStream<T>) -> Self {
695        stream.list.into()
696    }
697}
698
699impl<T: ListItem> DumpSection for ListStream<T> {
700    fn file_offset(&self) -> Label {
701        self.list.file_offset()
702    }
703
704    fn file_size(&self) -> Label {
705        self.list.file_size()
706    }
707}
708
709impl<T: ListItem> Stream for ListStream<T> {
710    fn stream_type(&self) -> u32 {
711        self.stream_type
712    }
713}
714
715pub struct Memory64ListStream {
716    section: Section,
717    count: u64,
718    count_label: Label,
719}
720
721impl Memory64ListStream {
722    pub fn new(endian: Endian, memory64_rva: &Label) -> Self {
723        let count_label = Label::new();
724        let section = Section::with_endian(endian)
725            .D64(&count_label)
726            .D64(memory64_rva);
727        Self {
728            section,
729            count: 0,
730            count_label,
731        }
732    }
733
734    pub fn add_memory(mut self, memory: &Memory) -> Self {
735        self.count += 1;
736        self.section = self.section.D64(memory.address).D64(memory.section.size());
737        self
738    }
739
740    pub fn is_empty(&self) -> bool {
741        self.count == 0
742    }
743}
744
745impl From<Memory64ListStream> for Section {
746    fn from(list: Memory64ListStream) -> Self {
747        // Finalize the entry count.
748        list.count_label.set_const(list.count);
749        list.section
750    }
751}
752
753impl DumpSection for Memory64ListStream {
754    fn file_offset(&self) -> Label {
755        self.section.file_offset()
756    }
757
758    fn file_size(&self) -> Label {
759        self.section.file_size()
760    }
761}
762
763impl Stream for Memory64ListStream {
764    fn stream_type(&self) -> u32 {
765        md::MINIDUMP_STREAM_TYPE::Memory64ListStream as u32
766    }
767}
768
769/// A stream containing a list of dump entries, using the extended header format.
770pub struct ExList<T: ListItem> {
771    /// The stream's contents.
772    section: Section,
773    /// The number of entries.
774    count: u32,
775    /// The number of entries, as a `Label`.
776    count_label: Label,
777    /// Out-of-band data referenced by this stream's contents.
778    out_of_band: Section,
779    _type: PhantomData<T>,
780}
781
782const EX_LIST_SIZE_OF_HEADER: usize = 12;
783
784impl<T: ListItem> ExList<T> {
785    pub fn new(size_of_entry: usize, endian: Endian) -> Self {
786        Self::new_with_header_size(EX_LIST_SIZE_OF_HEADER, size_of_entry, endian)
787    }
788
789    pub fn new_with_header_size(
790        size_of_header: usize,
791        size_of_entry: usize,
792        endian: Endian,
793    ) -> Self {
794        let count_label = Label::new();
795
796        // Newer list streams have an extended header:
797        //
798        // size_of_header: u32,
799        // size_of_entry: u32,
800        // number_of_entries: u32,
801        // ...entries
802
803        let padding = size_of_header - EX_LIST_SIZE_OF_HEADER;
804        let section = Section::with_endian(endian)
805            .D32(size_of_header as u32)
806            .D32(size_of_entry as u32)
807            .D32(&count_label)
808            .append_repeated(0, padding);
809
810        ExList {
811            section,
812            count_label,
813            count: 0,
814            out_of_band: Section::with_endian(endian),
815            _type: PhantomData,
816        }
817    }
818
819    // Possibly name this .add_section().
820    #[allow(clippy::should_implement_trait)]
821    pub fn add(mut self, entry: T) -> Self {
822        self.count += 1;
823
824        let (section, out_of_band_opt) = entry.into_sections();
825
826        self.section = self
827            .section
828            .mark(&section.file_offset())
829            .append_section(section);
830
831        if let Some(out_of_band) = out_of_band_opt {
832            self.out_of_band = self
833                .out_of_band
834                .mark(&out_of_band.file_offset())
835                .append_section(out_of_band);
836        }
837
838        self
839    }
840
841    pub fn is_empty(&self) -> bool {
842        self.count == 0
843    }
844}
845
846impl<T: ListItem> From<ExList<T>> for Section {
847    fn from(list: ExList<T>) -> Self {
848        // Finalize the entry count.
849        list.count_label.set_const(list.count as u64);
850
851        // Serialize all (transitive) out-of-band data after the dense list of entry records.
852        list.section
853            .mark(&list.out_of_band.file_offset())
854            .append_section(list.out_of_band)
855    }
856}
857
858impl<T: ListItem> DumpSection for ExList<T> {
859    fn file_offset(&self) -> Label {
860        self.section.file_offset()
861    }
862
863    fn file_size(&self) -> Label {
864        self.section.file_size()
865    }
866}
867
868pub struct ExListStream<T: ListItem> {
869    /// The stream type.
870    stream_type: u32,
871    /// The list containing items.
872    list: ExList<T>,
873}
874
875impl<T: ListItem> ExListStream<T> {
876    pub fn new<S: Into<u32>>(stream_type: S, size_of_entry: usize, endian: Endian) -> Self {
877        Self {
878            stream_type: stream_type.into(),
879            list: ExList::new(size_of_entry, endian),
880        }
881    }
882
883    pub fn new_with_header_size<S: Into<u32>>(
884        stream_type: S,
885        size_of_header: usize,
886        size_of_entry: usize,
887        endian: Endian,
888    ) -> Self {
889        Self {
890            stream_type: stream_type.into(),
891            list: ExList::new_with_header_size(size_of_header, size_of_entry, endian),
892        }
893    }
894
895    #[allow(clippy::should_implement_trait)]
896    pub fn add(mut self, entry: T) -> Self {
897        self.list = self.list.add(entry);
898        self
899    }
900
901    pub fn is_empty(&self) -> bool {
902        self.list.is_empty()
903    }
904}
905
906impl<T: ListItem> From<ExListStream<T>> for Section {
907    fn from(stream: ExListStream<T>) -> Self {
908        stream.list.into()
909    }
910}
911
912impl<T: ListItem> DumpSection for ExListStream<T> {
913    fn file_offset(&self) -> Label {
914        self.list.file_offset()
915    }
916
917    fn file_size(&self) -> Label {
918        self.list.file_size()
919    }
920}
921
922impl<T: ListItem> Stream for ExListStream<T> {
923    fn stream_type(&self) -> u32 {
924        self.stream_type
925    }
926}
927
928/// An `MINIDUMP_STRING`, a UTF-16 string preceded by a 4-byte length.
929pub struct DumpString {
930    section: Section,
931}
932
933impl DumpString {
934    /// Create a new `DumpString` with `s` as its contents, using `endian` endianness.
935    pub fn new(s: &str, endian: Endian) -> DumpString {
936        let u16_s = s
937            .encode_utf16()
938            .fold(Vec::with_capacity(s.len() * 2), |mut v, s| {
939                match endian {
940                    Endian::Little => {
941                        v.push((s & 0xff) as u8);
942                        v.push((s >> 8) as u8);
943                    }
944                    Endian::Big => {
945                        v.push((s >> 8) as u8);
946                        v.push((s & 0xff) as u8);
947                    }
948                }
949                v
950            });
951
952        let section = Section::with_endian(endian)
953            .D32(u16_s.len() as u32)
954            .append_bytes(&u16_s);
955        DumpString { section }
956    }
957}
958
959impl From<DumpString> for Section {
960    fn from(string: DumpString) -> Self {
961        string.section
962    }
963}
964
965impl_dumpsection!(DumpString);
966
967pub struct DumpUtf8String {
968    section: Section,
969}
970
971impl DumpUtf8String {
972    pub fn new(s: &str, endian: Endian) -> Self {
973        let section = Section::with_endian(endian)
974            .D32(s.len() as u32)
975            .append_bytes(s.as_bytes())
976            .D8(0);
977
978        Self { section }
979    }
980}
981
982impl From<DumpUtf8String> for Section {
983    fn from(string: DumpUtf8String) -> Self {
984        string.section
985    }
986}
987
988impl_dumpsection!(DumpUtf8String);
989
990/// A fixed set of version info to use for tests.
991pub const STOCK_VERSION_INFO: md::VS_FIXEDFILEINFO = md::VS_FIXEDFILEINFO {
992    signature: md::VS_FFI_SIGNATURE,
993    struct_version: md::VS_FFI_STRUCVERSION,
994    file_version_hi: 0x11111111,
995    file_version_lo: 0x22222222,
996    product_version_hi: 0x33333333,
997    product_version_lo: 0x44444444,
998    file_flags_mask: 1,
999    file_flags: 1,
1000    file_os: 0x40004,
1001    file_type: 1,
1002    file_subtype: 0,
1003    file_date_hi: 0,
1004    file_date_lo: 0,
1005};
1006
1007/// A minidump module.
1008pub struct Module {
1009    section: Section,
1010    cv_record: Option<(Label, Label)>,
1011    misc_record: Option<(Label, Label)>,
1012}
1013
1014impl Module {
1015    pub fn new<'a, T: Into<Option<&'a md::VS_FIXEDFILEINFO>>>(
1016        endian: Endian,
1017        base_of_image: u64,
1018        size_of_image: u32,
1019        name: &DumpString,
1020        time_date_stamp: u32,
1021        checksum: u32,
1022        version_info: T,
1023    ) -> Module {
1024        let stock_version = &STOCK_VERSION_INFO;
1025        let version_info = version_info.into().unwrap_or(stock_version);
1026        let section = Section::with_endian(endian)
1027            .D64(base_of_image)
1028            .D32(size_of_image)
1029            .D32(checksum)
1030            .D32(time_date_stamp)
1031            .D32(name.file_offset())
1032            .D32(version_info.signature)
1033            .D32(version_info.struct_version)
1034            .D32(version_info.file_version_hi)
1035            .D32(version_info.file_version_lo)
1036            .D32(version_info.product_version_hi)
1037            .D32(version_info.product_version_lo)
1038            .D32(version_info.file_flags_mask)
1039            .D32(version_info.file_flags)
1040            .D32(version_info.file_os)
1041            .D32(version_info.file_type)
1042            .D32(version_info.file_subtype)
1043            .D32(version_info.file_date_hi)
1044            .D32(version_info.file_date_lo);
1045        Module {
1046            section,
1047            cv_record: None,
1048            misc_record: None,
1049        }
1050    }
1051
1052    pub fn cv_record<T: DumpSection>(mut self, cv_record: &T) -> Module {
1053        self.cv_record = Some((cv_record.file_size(), cv_record.file_offset()));
1054        self
1055    }
1056
1057    pub fn misc_record<T: DumpSection>(mut self, misc_record: &T) -> Module {
1058        self.misc_record = Some((misc_record.file_size(), misc_record.file_offset()));
1059        self
1060    }
1061}
1062
1063impl_dumpsection!(Module);
1064
1065impl From<Module> for Section {
1066    fn from(module: Module) -> Self {
1067        let Module {
1068            section,
1069            cv_record,
1070            misc_record,
1071        } = module;
1072        section
1073            .cite_location(&cv_record)
1074            .cite_location(&misc_record)
1075            // reserved0
1076            .D64(0)
1077            // reserved1
1078            .D64(0)
1079    }
1080}
1081
1082/// A minidump unloaded module.
1083pub struct UnloadedModule {
1084    section: Section,
1085}
1086
1087impl UnloadedModule {
1088    pub fn new(
1089        endian: Endian,
1090        base_of_image: u64,
1091        size_of_image: u32,
1092        name: &DumpString,
1093        time_date_stamp: u32,
1094        checksum: u32,
1095    ) -> UnloadedModule {
1096        let section = Section::with_endian(endian)
1097            .D64(base_of_image)
1098            .D32(size_of_image)
1099            .D32(checksum)
1100            .D32(time_date_stamp)
1101            .D32(name.file_offset());
1102        UnloadedModule { section }
1103    }
1104}
1105
1106impl_dumpsection!(UnloadedModule);
1107
1108impl From<UnloadedModule> for Section {
1109    fn from(module: UnloadedModule) -> Self {
1110        let UnloadedModule { section } = module;
1111        section
1112    }
1113}
1114
1115/// A minidump thread.
1116pub struct Thread {
1117    section: Section,
1118}
1119
1120impl Thread {
1121    pub fn new<T>(endian: Endian, id: u32, stack: &Memory, context: &T) -> Thread
1122    where
1123        T: DumpSection,
1124    {
1125        let section = Section::with_endian(endian)
1126            .D32(id)
1127            .D32(0) // suspend_count
1128            .D32(0) // priority_class
1129            .D32(0) // priority
1130            .D64(0) // teb
1131            .cite_memory(stack)
1132            .cite_location(context);
1133        Thread { section }
1134    }
1135}
1136
1137impl_dumpsection!(Thread);
1138
1139impl From<Thread> for Section {
1140    fn from(thread: Thread) -> Self {
1141        thread.section
1142    }
1143}
1144
1145/// A minidump thread name.
1146pub struct ThreadName {
1147    section: Section,
1148}
1149
1150impl ThreadName {
1151    pub fn new(endian: Endian, id: u32, name: Option<&DumpString>) -> Self {
1152        let section = Section::with_endian(endian).D32(id);
1153        // Name is optional to test corrupt handles easily
1154        let section = if let Some(name) = name {
1155            section.D64(name.file_offset())
1156        } else {
1157            section.D64(0xFFFF_FFFF_FFFF_FFFF)
1158        };
1159        ThreadName { section }
1160    }
1161}
1162
1163impl_dumpsection!(ThreadName);
1164
1165impl From<ThreadName> for Section {
1166    fn from(thread: ThreadName) -> Self {
1167        thread.section
1168    }
1169}
1170
1171/// A range of memory contents.
1172pub struct Memory {
1173    section: Section,
1174    pub address: u64,
1175}
1176
1177impl Memory {
1178    /// Create a new `Memory` object representing memory starting at `address`,
1179    /// containing the contents of `section`.
1180    pub fn with_section(section: Section, address: u64) -> Memory {
1181        Memory { section, address }
1182    }
1183
1184    // Append an `MINIDUMP_MEMORY_DESCRIPTOR` referring to this memory range to `section`.
1185    pub fn cite_memory_in(&self, section: Section) -> Section {
1186        section.D64(self.address).cite_location(self)
1187    }
1188}
1189
1190impl_dumpsection!(Memory);
1191
1192impl From<Memory> for Section {
1193    fn from(memory: Memory) -> Self {
1194        memory.section
1195    }
1196}
1197
1198/// A minidump memory information element.
1199pub struct MemoryInfo {
1200    section: Section,
1201}
1202
1203impl MemoryInfo {
1204    #[allow(clippy::too_many_arguments)]
1205    pub fn new(
1206        endian: Endian,
1207        base_address: u64,
1208        allocation_base: u64,
1209        allocation_protection: u32,
1210        region_size: u64,
1211        state: u32,
1212        protection: u32,
1213        ty: u32,
1214    ) -> MemoryInfo {
1215        let section = Section::with_endian(endian)
1216            .D64(base_address)
1217            .D64(allocation_base)
1218            .D32(allocation_protection)
1219            .D32(0) // __alignment1
1220            .D64(region_size)
1221            .D32(state)
1222            .D32(protection)
1223            .D32(ty)
1224            .D32(0); // __alignment2
1225        MemoryInfo { section }
1226    }
1227}
1228
1229impl_dumpsection!(MemoryInfo);
1230
1231impl From<MemoryInfo> for Section {
1232    fn from(info: MemoryInfo) -> Self {
1233        info.section
1234    }
1235}
1236
1237/// A minidump handle descriptor.
1238pub struct HandleDescriptor {
1239    section: Section,
1240}
1241
1242impl HandleDescriptor {
1243    #[allow(clippy::too_many_arguments)]
1244    pub fn new(
1245        endian: Endian,
1246        handle: u64,
1247        type_name: Option<&DumpString>,
1248        object_name: Option<&DumpString>,
1249        attributes: u32,
1250        granted_access: u32,
1251        handle_count: u32,
1252        pointer_count: u32,
1253    ) -> HandleDescriptor {
1254        let type_name_rva = type_name.map_or(Label::from_const(0), |t| t.file_offset());
1255        let object_name_rva = object_name.map_or(Label::from_const(0), |o| o.file_offset());
1256        let section = Section::with_endian(endian)
1257            .D64(handle)
1258            .D32(type_name_rva)
1259            .D32(object_name_rva)
1260            .D32(attributes)
1261            .D32(granted_access)
1262            .D32(handle_count)
1263            .D32(pointer_count);
1264        HandleDescriptor { section }
1265    }
1266}
1267
1268impl_dumpsection!(HandleDescriptor);
1269
1270impl From<HandleDescriptor> for Section {
1271    fn from(descriptor: HandleDescriptor) -> Self {
1272        descriptor.section
1273    }
1274}
1275
1276/// MINIDUMP_MISC_INFO stream.
1277///
1278/// Fields that must be initialized together (i.e. because they are guarded
1279/// by the same flag) are grouped under substructs to enforce this.
1280pub struct MiscStream {
1281    /// The stream's contents.
1282    section: Section,
1283
1284    /// MISC_INFO field guarded by MINIDUMP_MISC1_PROCESS_ID
1285    pub process_id: Option<u32>,
1286    /// MISC_INFO fields guarded by MINIDUMP_MISC1_PROCESS_TIMES
1287    pub process_times: Option<MiscFieldsProcessTimes>,
1288
1289    /// MISC_INFO_2 fields guarded by MINIDUMP_MISC1_PROCESSOR_POWER_INFO
1290    pub power_info: Option<MiscFieldsPowerInfo>,
1291
1292    /// MISC_INFO_3 field guarded by MINIDUMP_MISC3_PROCESS_INTEGRITY
1293    pub process_integrity_level: Option<u32>,
1294    /// MISC_INFO_3 field guarded by MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS
1295    pub process_execute_flags: Option<u32>,
1296    /// MISC_INFO_3 field guarded by MINIDUMP_MISC3_PROTECTED_PROCESS
1297    pub protected_process: Option<u32>,
1298    /// MISC_INFO_3 fields guarded by MINIDUMP_MISC3_TIMEZONE
1299    pub time_zone: Option<MiscFieldsTimeZone>,
1300
1301    /// MISC_INFO_4 fields guarded by MINIDUMP_MISC4_BUILDSTRING
1302    pub build_strings: Option<MiscFieldsBuildString>,
1303
1304    /// MISC_INFO_5 fields
1305    pub misc_5: Option<MiscInfo5Fields>,
1306
1307    pub pad_to_size: Option<usize>,
1308}
1309
1310/// MISC_INFO fields guardard by MINIDUMP_MISC1_PROCESS_TIMES
1311#[derive(Default)]
1312pub struct MiscFieldsProcessTimes {
1313    pub process_create_time: u32,
1314    pub process_user_time: u32,
1315    pub process_kernel_time: u32,
1316}
1317
1318/// MISC_INFO_2 fields guarded by MINIDUMP_MISC1_PROCESSOR_POWER_INFO
1319#[derive(Default)]
1320pub struct MiscFieldsPowerInfo {
1321    pub processor_max_mhz: u32,
1322    pub processor_current_mhz: u32,
1323    pub processor_mhz_limit: u32,
1324    pub processor_max_idle_state: u32,
1325    pub processor_current_idle_state: u32,
1326}
1327
1328/// MISC_INFO_3 fields guarded by MINIDUMP_MISC3_TIMEZONE
1329#[derive(Default)]
1330pub struct MiscFieldsTimeZone {
1331    pub time_zone_id: u32,
1332    pub time_zone: md::TIME_ZONE_INFORMATION,
1333}
1334
1335/// MISC_INFO_4 fields guarded by MINIDUMP_MISC4_BUILDSTRING
1336pub struct MiscFieldsBuildString {
1337    pub build_string: [u16; 260],
1338    pub dbg_bld_str: [u16; 40],
1339}
1340
1341impl Default for MiscFieldsBuildString {
1342    fn default() -> Self {
1343        Self {
1344            build_string: [0; 260],
1345            dbg_bld_str: [0; 40],
1346        }
1347    }
1348}
1349
1350/// MISC_INFO_5 fields (xstate_data must exist if process_cookie is set).
1351#[derive(Default)]
1352pub struct MiscInfo5Fields {
1353    pub xstate_data: md::XSTATE_CONFIG_FEATURE_MSC_INFO,
1354    /// MISC_INFO_5 field guarded by MINIDUMP_MISC5_PROCESS_COOKIE
1355    pub process_cookie: Option<u32>,
1356}
1357
1358impl MiscStream {
1359    pub fn new(endian: Endian) -> MiscStream {
1360        let section = Section::with_endian(endian);
1361        let size = section.final_size();
1362        MiscStream {
1363            section: section.D32(size),
1364            process_id: None,
1365            process_times: None,
1366            power_info: None,
1367            process_integrity_level: None,
1368            process_execute_flags: None,
1369            protected_process: None,
1370            time_zone: None,
1371            build_strings: None,
1372            misc_5: None,
1373            pad_to_size: None,
1374        }
1375    }
1376}
1377
1378impl From<MiscStream> for Section {
1379    fn from(stream: MiscStream) -> Self {
1380        let MiscStream {
1381            section,
1382
1383            process_id,
1384            process_times,
1385
1386            power_info,
1387
1388            process_integrity_level,
1389            process_execute_flags,
1390            protected_process,
1391            time_zone,
1392
1393            build_strings,
1394
1395            misc_5,
1396
1397            pad_to_size,
1398        } = stream;
1399
1400        // Derive the flags and misc_info version we'll be using.
1401        let mut misc_info_version = 1;
1402        let mut flags = md::MiscInfoFlags::empty();
1403
1404        if process_id.is_some() {
1405            flags |= md::MiscInfoFlags::MINIDUMP_MISC1_PROCESS_ID;
1406        }
1407        if process_times.is_some() {
1408            flags |= md::MiscInfoFlags::MINIDUMP_MISC1_PROCESS_TIMES;
1409        }
1410
1411        if power_info.is_some() {
1412            flags |= md::MiscInfoFlags::MINIDUMP_MISC1_PROCESSOR_POWER_INFO;
1413            misc_info_version = 2;
1414        }
1415
1416        if process_integrity_level.is_some() {
1417            flags |= md::MiscInfoFlags::MINIDUMP_MISC3_PROCESS_INTEGRITY;
1418            misc_info_version = 3;
1419        }
1420        if process_execute_flags.is_some() {
1421            flags |= md::MiscInfoFlags::MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS;
1422            misc_info_version = 3;
1423        }
1424        if protected_process.is_some() {
1425            flags |= md::MiscInfoFlags::MINIDUMP_MISC3_PROTECTED_PROCESS;
1426            misc_info_version = 3;
1427        }
1428        if time_zone.is_some() {
1429            flags |= md::MiscInfoFlags::MINIDUMP_MISC3_TIMEZONE;
1430            misc_info_version = 3;
1431        }
1432
1433        if build_strings.is_some() {
1434            flags |= md::MiscInfoFlags::MINIDUMP_MISC4_BUILDSTRING;
1435            misc_info_version = 4;
1436        }
1437
1438        if let Some(ref misc_5) = misc_5 {
1439            if misc_5.process_cookie.is_some() {
1440                flags |= md::MiscInfoFlags::MINIDUMP_MISC5_PROCESS_COOKIE
1441            }
1442            misc_info_version = 5;
1443        }
1444
1445        // Now that we know what version we are, emit all the fields necessary
1446        // for that version, leaning on Default to fill in values that are None.
1447        let mut section = section.D32(flags.bits() as u64 as u32);
1448
1449        let process_id = process_id.unwrap_or_default();
1450        let process_times = process_times.unwrap_or_default();
1451        section = section.D32(process_id);
1452        section = section
1453            .D32(process_times.process_create_time)
1454            .D32(process_times.process_user_time)
1455            .D32(process_times.process_kernel_time);
1456
1457        if misc_info_version >= 2 {
1458            let power_info = power_info.unwrap_or_default();
1459            section = section
1460                .D32(power_info.processor_max_mhz)
1461                .D32(power_info.processor_current_mhz)
1462                .D32(power_info.processor_mhz_limit)
1463                .D32(power_info.processor_max_idle_state)
1464                .D32(power_info.processor_current_idle_state);
1465        }
1466
1467        if misc_info_version >= 3 {
1468            let process_integrity_level = process_integrity_level.unwrap_or_default();
1469            let process_execute_flags = process_execute_flags.unwrap_or_default();
1470            let protected_process = protected_process.unwrap_or_default();
1471            let time_zone = time_zone.unwrap_or_default();
1472
1473            section = section.D32(process_integrity_level);
1474            section = section.D32(process_execute_flags);
1475            section = section.D32(protected_process);
1476
1477            fn write_system_time(section: Section, time: &md::SYSTEMTIME) -> Section {
1478                section
1479                    .D16(time.year)
1480                    .D16(time.month)
1481                    .D16(time.day_of_week)
1482                    .D16(time.day)
1483                    .D16(time.hour)
1484                    .D16(time.minute)
1485                    .D16(time.second)
1486                    .D16(time.milliseconds)
1487            }
1488
1489            section = section.D32(time_zone.time_zone_id);
1490            let time_zone = time_zone.time_zone;
1491            section = section.D32(time_zone.bias as u32);
1492            for &val in &time_zone.standard_name {
1493                section = section.D16(val);
1494            }
1495            section = write_system_time(section, &time_zone.standard_date);
1496            section = section.D32(time_zone.standard_bias as u32);
1497            for &val in &time_zone.daylight_name {
1498                section = section.D16(val);
1499            }
1500            section = write_system_time(section, &time_zone.daylight_date);
1501            section = section.D32(time_zone.daylight_bias as u32);
1502        }
1503
1504        if misc_info_version >= 4 {
1505            let build_strings = build_strings.unwrap_or_default();
1506            for &val in &build_strings.build_string {
1507                section = section.D16(val);
1508            }
1509            for &val in &build_strings.dbg_bld_str {
1510                section = section.D16(val);
1511            }
1512        }
1513
1514        if misc_info_version >= 5 {
1515            let misc_5 = misc_5.unwrap_or_default();
1516            let process_cookie = misc_5.process_cookie.unwrap_or_default();
1517            let xstate = misc_5.xstate_data;
1518            section = section
1519                .D32(xstate.size_of_info)
1520                .D32(xstate.context_size)
1521                .D64(xstate.enabled_features);
1522
1523            for feature in &xstate.features {
1524                section = section.D32(feature.offset).D32(feature.size);
1525            }
1526            section = section.D32(process_cookie);
1527        }
1528
1529        // Pad to final size, if necessary.
1530        if let Some(size) = pad_to_size {
1531            let size = (size as u64 - section.size()) as usize;
1532            section.append_repeated(0, size)
1533        } else {
1534            section
1535        }
1536    }
1537}
1538
1539impl_dumpsection!(MiscStream);
1540
1541impl Stream for MiscStream {
1542    fn stream_type(&self) -> u32 {
1543        md::MINIDUMP_STREAM_TYPE::MiscInfoStream as u32
1544    }
1545}
1546
1547/// Populate a `CONTEXT_X86` struct with the given `endian`, `eip`, and `esp`.
1548pub fn x86_context(endian: Endian, eip: u32, esp: u32) -> Section {
1549    let section = Section::with_endian(endian)
1550        .D32(0x1003f) // context_flags: CONTEXT_X86_ALL
1551        .append_repeated(0, 4 * 6) // dr0,1,2,3,6,7, 4 bytes each
1552        .append_repeated(0, md::FLOATING_SAVE_AREA_X86::size_with(&LE)) // float_save
1553        .append_repeated(0, 4 * 11) // gs-ebp, 4 bytes each
1554        .D32(eip)
1555        .D32(0) // cs
1556        .D32(0) // eflags
1557        .D32(esp)
1558        .D32(0) // ss
1559        .append_repeated(0, 512); // extended_registers
1560    assert_eq!(section.size(), md::CONTEXT_X86::size_with(&LE) as u64);
1561    section
1562}
1563
1564/// Populate a `CONTEXT_AMD64` struct with the given `endian`, `rip`, and `rsp`.
1565pub fn amd64_context(endian: Endian, rip: u64, rsp: u64) -> Section {
1566    let section = Section::with_endian(endian)
1567        .append_repeated(0, mem::size_of::<u64>() * 6) // p[1-6]_home
1568        .D32(0x10001f) // context_flags: CONTEXT_AMD64_ALL
1569        .D32(0) // mx_csr
1570        .append_repeated(0, mem::size_of::<u16>() * 6) // cs,ds,es,fs,gs,ss
1571        .D32(0) // eflags
1572        .append_repeated(0, mem::size_of::<u64>() * 6) // dr0,1,2,3,6,7
1573        .append_repeated(0, mem::size_of::<u64>() * 4) // rax,rcx,rdx,rbx
1574        .D64(rsp)
1575        .append_repeated(0, mem::size_of::<u64>() * 11) // rbp-r15
1576        .D64(rip)
1577        .append_repeated(0, 512) // float_save
1578        .append_repeated(0, mem::size_of::<u128>() * 26) // vector_register
1579        .append_repeated(0, mem::size_of::<u64>() * 6); // trailing stuff
1580    assert_eq!(section.size(), md::CONTEXT_AMD64::size_with(&LE) as u64);
1581    section
1582}
1583
1584/// Populate a `CONTEXT_ARM64` struct with the given `endian`, `pc`, and `sp`.
1585pub fn arm64_context(endian: Endian, pc: u64, sp: u64) -> Section {
1586    let section = Section::with_endian(endian)
1587        .D32(0x40001f) // context_flags: CONTEXT_ARM64_ALL
1588        .D32(0) // cpsr
1589        .append_repeated(0, mem::size_of::<u64>() * 31) // iregs[x0, x1, ..., x28, fp, lr],
1590        .D64(sp) // sp
1591        .D64(pc) // pc
1592        .append_repeated(0, mem::size_of::<u128>() * 32) // float_regs[d0, d1, ..., d31]
1593        .D32(0) // fpcr
1594        .D32(0) // fpsr
1595        .append_repeated(0, mem::size_of::<u32>() * 8) // bcr[0-7]
1596        .append_repeated(0, mem::size_of::<u64>() * 8) // bvr[0-7]
1597        .append_repeated(0, mem::size_of::<u32>() * 2) // wcr[0-1]
1598        .append_repeated(0, mem::size_of::<u64>() * 2); // wvr[0-1]
1599    assert_eq!(section.size(), md::CONTEXT_ARM64::size_with(&LE) as u64);
1600    section
1601}
1602
1603pub struct SectionRef {
1604    section: Section,
1605    data_section: Section,
1606}
1607
1608impl SectionRef {
1609    pub fn new(data_section: impl Into<Section>, endian: Endian) -> Self {
1610        let data_section = data_section.into();
1611        let section = Section::with_endian(endian).D32(data_section.file_offset());
1612        Self {
1613            section,
1614            data_section,
1615        }
1616    }
1617}
1618
1619impl_dumpsection!(SectionRef);
1620
1621impl ListItem for SectionRef {
1622    fn into_sections(self) -> (Section, Option<Section>) {
1623        (self.section, Some(self.data_section))
1624    }
1625}
1626
1627pub struct SimpleStringDictionaryEntry {
1628    endian: Endian,
1629    section: Section,
1630    key: DumpUtf8String,
1631    value: DumpUtf8String,
1632}
1633
1634impl SimpleStringDictionaryEntry {
1635    pub fn new(key: &str, value: &str, endian: Endian) -> Self {
1636        Self {
1637            endian,
1638            section: Section::with_endian(endian),
1639            key: DumpUtf8String::new(key, endian),
1640            value: DumpUtf8String::new(value, endian),
1641        }
1642    }
1643}
1644
1645impl_dumpsection!(SimpleStringDictionaryEntry);
1646
1647impl ListItem for SimpleStringDictionaryEntry {
1648    fn into_sections(self) -> (Section, Option<Section>) {
1649        let section = self
1650            .section
1651            .D32(self.key.file_offset())
1652            .D32(self.value.file_offset());
1653
1654        let out_of_band = Section::with_endian(self.endian)
1655            .mark(&self.key.file_offset())
1656            .append_section(self.key)
1657            .mark(&self.value.file_offset())
1658            .append_section(self.value);
1659
1660        (section, Some(out_of_band))
1661    }
1662}
1663
1664pub type SimpleStringDictionary = List<SimpleStringDictionaryEntry>;
1665
1666#[derive(Clone, Debug)]
1667pub enum AnnotationValue {
1668    Invalid,
1669    String(String),
1670    Custom(u16, Vec<u8>),
1671}
1672
1673pub struct AnnotationObject {
1674    section: Section,
1675    out_of_band: Section,
1676}
1677
1678impl AnnotationObject {
1679    pub fn new(name: &str, value: AnnotationValue, endian: Endian) -> Self {
1680        let name = DumpUtf8String::new(name, endian);
1681
1682        let (ty, value) = match value {
1683            AnnotationValue::Invalid => (md::MINIDUMP_ANNOTATION::TYPE_INVALID, None),
1684            AnnotationValue::String(s) => (
1685                md::MINIDUMP_ANNOTATION::TYPE_STRING,
1686                Some(DumpUtf8String::new(&s, endian).into()),
1687            ),
1688            AnnotationValue::Custom(ty, bytes) => (ty, Some(Section::new().append_bytes(&bytes))),
1689        };
1690
1691        let mut section = Section::with_endian(endian)
1692            .D32(name.file_offset())
1693            .D16(ty)
1694            .D16(0); // reserved, always 0
1695
1696        section = match value {
1697            Some(ref value) => section.D32(value.file_offset()),
1698            None => section.D32(0),
1699        };
1700
1701        let mut out_of_band = Section::with_endian(endian)
1702            .mark(&name.file_offset())
1703            .append_section(name);
1704
1705        if let Some(value) = value {
1706            out_of_band = out_of_band.mark(&value.file_offset()).append_section(value);
1707        }
1708
1709        Self {
1710            section,
1711            out_of_band,
1712        }
1713    }
1714}
1715
1716impl_dumpsection!(AnnotationObject);
1717
1718impl ListItem for AnnotationObject {
1719    fn into_sections(self) -> (Section, Option<Section>) {
1720        (self.section, Some(self.out_of_band))
1721    }
1722}
1723
1724pub type AnnotationObjects = List<AnnotationObject>;
1725
1726/// Link + Info
1727pub struct ModuleCrashpadInfo {
1728    endian: Endian,
1729    section: Section,
1730    list_annotations: List<SectionRef>,
1731    simple_annotations: SimpleStringDictionary,
1732    annotation_objects: AnnotationObjects,
1733}
1734
1735impl ModuleCrashpadInfo {
1736    pub fn new(index: u32, endian: Endian) -> Self {
1737        Self {
1738            endian,
1739            section: Section::with_endian(endian).D32(index),
1740            list_annotations: List::new(endian),
1741            simple_annotations: SimpleStringDictionary::new(endian),
1742            annotation_objects: AnnotationObjects::new(endian),
1743        }
1744    }
1745
1746    pub fn add_list_annotation(mut self, value: &str) -> Self {
1747        let section = SectionRef::new(DumpUtf8String::new(value, self.endian), self.endian);
1748        self.list_annotations = self.list_annotations.add(section);
1749        self
1750    }
1751
1752    pub fn add_simple_annotation(mut self, key: &str, value: &str) -> Self {
1753        let entry = SimpleStringDictionaryEntry::new(key, value, self.endian);
1754        self.simple_annotations = self.simple_annotations.add(entry);
1755        self
1756    }
1757
1758    pub fn add_annotation_object(mut self, key: &str, value: AnnotationValue) -> Self {
1759        let object = AnnotationObject::new(key, value, self.endian);
1760        self.annotation_objects = self.annotation_objects.add(object);
1761        self
1762    }
1763}
1764
1765impl_dumpsection!(ModuleCrashpadInfo);
1766
1767impl ListItem for ModuleCrashpadInfo {
1768    fn into_sections(self) -> (Section, Option<Section>) {
1769        let info = Section::with_endian(self.endian)
1770            .D32(md::MINIDUMP_MODULE_CRASHPAD_INFO::VERSION)
1771            .cite_location(&self.list_annotations)
1772            .cite_location(&self.simple_annotations)
1773            .cite_location(&self.annotation_objects)
1774            .mark(&self.list_annotations.file_offset())
1775            .append_section(self.list_annotations)
1776            .mark(&self.simple_annotations.file_offset())
1777            .append_section(self.simple_annotations)
1778            .mark(&self.annotation_objects.file_offset())
1779            .append_section(self.annotation_objects);
1780
1781        let link = self.section.cite_location(&info);
1782
1783        (link, Some(info))
1784    }
1785}
1786
1787pub type ModuleCrashpadInfoList = List<ModuleCrashpadInfo>;
1788
1789pub struct Guid {
1790    section: Section,
1791}
1792
1793impl Guid {
1794    pub fn new(guid: md::GUID, endian: Endian) -> Self {
1795        let section = Section::with_endian(endian)
1796            .D32(guid.data1)
1797            .D16(guid.data2)
1798            .D16(guid.data3)
1799            .append_bytes(&guid.data4);
1800
1801        Self { section }
1802    }
1803
1804    pub fn empty(endian: Endian) -> Self {
1805        let guid = md::GUID {
1806            data1: 0,
1807            data2: 0,
1808            data3: 0,
1809            data4: [0, 0, 0, 0, 0, 0, 0, 0],
1810        };
1811
1812        Self::new(guid, endian)
1813    }
1814}
1815
1816// Guid does not impl DumpSections as it cannot be cited.
1817
1818impl From<Guid> for Section {
1819    fn from(guid: Guid) -> Self {
1820        guid.section
1821    }
1822}
1823
1824pub struct CrashpadInfo {
1825    endian: Endian,
1826    section: Section,
1827    report_id: Guid,
1828    client_id: Guid,
1829    simple_annotations: SimpleStringDictionary,
1830    module_list: ModuleCrashpadInfoList,
1831}
1832
1833impl CrashpadInfo {
1834    pub fn new(endian: Endian) -> Self {
1835        Self {
1836            endian,
1837            section: Section::with_endian(endian),
1838            report_id: Guid::empty(endian),
1839            client_id: Guid::empty(endian),
1840            simple_annotations: SimpleStringDictionary::new(endian),
1841            module_list: ModuleCrashpadInfoList::new(endian),
1842        }
1843    }
1844
1845    pub fn report_id(mut self, report_id: md::GUID) -> Self {
1846        self.report_id = Guid::new(report_id, self.endian);
1847        self
1848    }
1849
1850    pub fn client_id(mut self, client_id: md::GUID) -> Self {
1851        self.client_id = Guid::new(client_id, self.endian);
1852        self
1853    }
1854
1855    pub fn add_simple_annotation(mut self, key: &str, value: &str) -> Self {
1856        let entry = SimpleStringDictionaryEntry::new(key, value, self.endian);
1857        self.simple_annotations = self.simple_annotations.add(entry);
1858        self
1859    }
1860
1861    pub fn add_module(mut self, info: ModuleCrashpadInfo) -> Self {
1862        self.module_list = self.module_list.add(info);
1863        self
1864    }
1865}
1866
1867impl_dumpsection!(CrashpadInfo);
1868
1869impl From<CrashpadInfo> for Section {
1870    fn from(info: CrashpadInfo) -> Self {
1871        info.section
1872            .D32(md::MINIDUMP_CRASHPAD_INFO::VERSION)
1873            .append_section(info.report_id)
1874            .append_section(info.client_id)
1875            .cite_location(&info.simple_annotations)
1876            .cite_location(&info.module_list)
1877            .mark(&info.simple_annotations.file_offset())
1878            .append_section(info.simple_annotations)
1879            .mark(&info.module_list.file_offset())
1880            .append_section(info.module_list)
1881    }
1882}
1883
1884impl Stream for CrashpadInfo {
1885    fn stream_type(&self) -> u32 {
1886        md::MINIDUMP_STREAM_TYPE::CrashpadInfoStream.into()
1887    }
1888}
1889
1890// Hastily stubbed out to just barely work
1891pub struct SystemInfo {
1892    section: Section,
1893    pub processor_architecture: u16,
1894    pub processor_level: u16,
1895    pub processor_revision: u16,
1896    pub number_of_processors: u8,
1897    pub product_type: u8,
1898    pub major_version: u32,
1899    pub minor_version: u32,
1900    pub build_number: u32,
1901    pub platform_id: u32,
1902    pub csd_version_rva: u32,
1903    pub suite_mask: u16,
1904    pub reserved2: u16,
1905    pub cpu: CpuInfo,
1906}
1907
1908pub enum CpuInfo {
1909    // Note, even if you're not on x86 this is a fine default.
1910    X86CpuInfo {
1911        vendor_id: [u32; 3],
1912        version_information: u32,
1913        feature_information: u32,
1914        amd_extended_cpu_features: u32,
1915    },
1916}
1917
1918impl SystemInfo {
1919    pub fn new(endian: Endian) -> Self {
1920        Self {
1921            section: Section::with_endian(endian),
1922            processor_architecture: 0,
1923            processor_level: 6,
1924            processor_revision: 0x0000,
1925            number_of_processors: 1,
1926            product_type: 0,
1927            major_version: 0,
1928            minor_version: 0,
1929            build_number: 0,
1930            platform_id: 0,
1931            csd_version_rva: 0,
1932            suite_mask: 0,
1933            reserved2: 0,
1934            cpu: CpuInfo::X86CpuInfo {
1935                vendor_id: [0; 3],
1936                version_information: 0,
1937                feature_information: 0,
1938                amd_extended_cpu_features: 0,
1939            },
1940        }
1941    }
1942
1943    pub fn set_processor_architecture(mut self, arch: u16) -> Self {
1944        self.processor_architecture = arch;
1945        self
1946    }
1947
1948    pub fn set_platform_id(mut self, platform_id: u32) -> Self {
1949        self.platform_id = platform_id;
1950        self
1951    }
1952}
1953
1954impl_dumpsection!(SystemInfo);
1955
1956impl From<SystemInfo> for Section {
1957    fn from(info: SystemInfo) -> Self {
1958        let section = info
1959            .section
1960            .D16(info.processor_architecture)
1961            .D16(info.processor_level)
1962            .D16(info.processor_revision)
1963            .D8(info.number_of_processors)
1964            .D8(info.product_type)
1965            .D32(info.major_version)
1966            .D32(info.minor_version)
1967            .D32(info.build_number)
1968            .D32(info.platform_id)
1969            .D32(info.csd_version_rva)
1970            .D16(info.suite_mask)
1971            .D16(info.reserved2);
1972
1973        match info.cpu {
1974            CpuInfo::X86CpuInfo {
1975                vendor_id,
1976                version_information,
1977                feature_information,
1978                amd_extended_cpu_features,
1979            } => section
1980                .D32(vendor_id[0])
1981                .D32(vendor_id[1])
1982                .D32(vendor_id[2])
1983                .D32(version_information)
1984                .D32(feature_information)
1985                .D32(amd_extended_cpu_features),
1986        }
1987    }
1988}
1989
1990impl Stream for SystemInfo {
1991    fn stream_type(&self) -> u32 {
1992        md::MINIDUMP_STREAM_TYPE::SystemInfoStream.into()
1993    }
1994}
1995
1996pub struct Exception {
1997    section: Section,
1998    pub thread_id: u32,
1999    // __align: u32,
2000    pub exception_record: ExceptionRecord,
2001    // TODO: implement this LOCATION_DESCRIPTOR properly
2002    pub thread_context: (u32, u32),
2003}
2004
2005pub struct ExceptionRecord {
2006    pub exception_code: u32,
2007    pub exception_flags: u32,
2008    pub exception_record: u64,
2009    pub exception_address: u64,
2010    pub number_parameters: u32,
2011    // __align: u32,
2012    pub exception_information: [u64; 15],
2013}
2014
2015impl Exception {
2016    pub fn new(endian: Endian) -> Self {
2017        Self {
2018            section: Section::with_endian(endian),
2019            thread_id: 0,
2020            exception_record: ExceptionRecord {
2021                exception_code: 0,
2022                exception_flags: 0,
2023                exception_record: 0,
2024                exception_address: 0,
2025                number_parameters: 0,
2026                exception_information: [0; 15],
2027            },
2028            thread_context: (0, 0),
2029        }
2030    }
2031}
2032
2033impl_dumpsection!(Exception);
2034
2035impl From<Exception> for Section {
2036    fn from(info: Exception) -> Self {
2037        let mut section = info
2038            .section
2039            .D32(info.thread_id)
2040            .D32(0) // __align
2041            .D32(info.exception_record.exception_code)
2042            .D32(info.exception_record.exception_flags)
2043            .D64(info.exception_record.exception_record)
2044            .D64(info.exception_record.exception_address)
2045            .D32(info.exception_record.number_parameters)
2046            .D32(0); // __align
2047
2048        for &chunk in &info.exception_record.exception_information {
2049            section = section.D64(chunk);
2050        }
2051
2052        section = section
2053            .D32(info.thread_context.0)
2054            .D32(info.thread_context.1);
2055
2056        section
2057    }
2058}
2059
2060impl Stream for Exception {
2061    fn stream_type(&self) -> u32 {
2062        md::MINIDUMP_STREAM_TYPE::ExceptionStream.into()
2063    }
2064}
2065
2066#[test]
2067fn test_dump_header() {
2068    let dump = SynthMinidump::with_endian(Endian::Little).flags(0x9f738b33685cc84c);
2069    assert_eq!(
2070        dump.finish().unwrap(),
2071        vec![
2072            0x4d, 0x44, 0x4d, 0x50, // signature
2073            0x93, 0xa7, 0x00, 0x00, // version
2074            0, 0, 0, 0, // stream count
2075            0x20, 0, 0, 0, // directory RVA
2076            0, 0, 0, 0, // checksum
2077            0x3d, 0xe1, 0x44, 0x4b, // time_date_stamp
2078            0x4c, 0xc8, 0x5c, 0x68, // flags
2079            0x33, 0x8b, 0x73, 0x9f,
2080        ]
2081    );
2082}
2083
2084#[test]
2085fn test_dump_header_bigendian() {
2086    let dump = SynthMinidump::with_endian(Endian::Big).flags(0x9f738b33685cc84c);
2087    assert_eq!(
2088        dump.finish().unwrap(),
2089        vec![
2090            0x50, 0x4d, 0x44, 0x4d, // signature
2091            0x00, 0x00, 0xa7, 0x93, // version
2092            0, 0, 0, 0, // stream count
2093            0, 0, 0, 0x20, // directory RVA
2094            0, 0, 0, 0, // checksum
2095            0x4b, 0x44, 0xe1, 0x3d, // time_date_stamp
2096            0x9f, 0x73, 0x8b, 0x33, // flags
2097            0x68, 0x5c, 0xc8, 0x4c,
2098        ]
2099    );
2100}
2101
2102#[test]
2103fn test_section_cite() {
2104    let s1 = Section::with_endian(Endian::Little).append_repeated(0, 0x0a);
2105    s1.start().set_const(0xff00ee11);
2106    let s2 = Section::with_endian(Endian::Little);
2107    let s2 = s1.cite_location_in(s2);
2108    s1.get_contents().unwrap();
2109    assert_eq!(
2110        s2.get_contents().unwrap(),
2111        vec![0x0a, 0, 0, 0, 0x11, 0xee, 0x00, 0xff]
2112    );
2113}
2114
2115#[test]
2116fn test_dump_string() {
2117    let dump = SynthMinidump::with_endian(Endian::Little);
2118    let s = DumpString::new("hello", Endian::Little);
2119    let contents = dump.add(s).finish().unwrap();
2120    // Skip over the header
2121    assert_eq!(
2122        &contents[mem::size_of::<md::MINIDUMP_HEADER>()..],
2123        &[
2124            0xa, 0x0, 0x0, 0x0, // length
2125            b'h', 0x0, b'e', 0x0, b'l', 0x0, b'l', 0x0, b'o', 0x0
2126        ]
2127    );
2128}
2129
2130#[test]
2131fn test_list_out_of_band() {
2132    let list = List::<SectionRef>::new(Endian::Little);
2133    assert_eq!(
2134        Into::<Section>::into(list).get_contents().unwrap(),
2135        vec![0, 0, 0, 0]
2136    );
2137
2138    let a = SectionRef::new(DumpUtf8String::new("foo", Endian::Little), Endian::Little);
2139    let b = SectionRef::new(DumpUtf8String::new("bar", Endian::Little), Endian::Little);
2140    let section: Section = List::new(Endian::Little).add(a).add(b).into();
2141    assert_eq!(
2142        section.set_start_const(0).get_contents().unwrap(),
2143        vec![
2144            2, 0, 0, 0, // entry count
2145            12, 0, 0, 0, // first RVA
2146            20, 0, 0, 0, // second RVA
2147            3, 0, 0, 0, // "foo".len()
2148            102, 111, 111, 0, // "foo\0"
2149            3, 0, 0, 0, // "bar".len()
2150            98, 97, 114, 0 // "bar\0"
2151        ]
2152    );
2153}
2154
2155#[test]
2156fn test_list_stream() {
2157    // Empty list
2158    let list = ListStream::<DumpString>::new(0x11223344u32, Endian::Little);
2159    assert_eq!(
2160        Into::<Section>::into(list).get_contents().unwrap(),
2161        vec![0, 0, 0, 0]
2162    );
2163    let list = ListStream::new(0x11223344u32, Endian::Little)
2164        .add(DumpString::new("a", Endian::Little))
2165        .add(DumpString::new("b", Endian::Little));
2166    assert_eq!(
2167        Into::<Section>::into(list).get_contents().unwrap(),
2168        vec![
2169            2, 0, 0, 0, // entry count
2170            // first entry
2171            0x2, 0x0, 0x0, 0x0, // length
2172            b'a', 0x0, // second entry
2173            0x2, 0x0, 0x0, 0x0, // length
2174            b'b', 0x0
2175        ]
2176    );
2177}
2178
2179#[test]
2180fn test_simple_stream() {
2181    let section = Section::with_endian(Endian::Little).D32(0x55667788);
2182    let stream_rva = mem::size_of::<md::MINIDUMP_HEADER>() as u8;
2183    let directory_rva = stream_rva + section.size() as u8;
2184    let dump = SynthMinidump::with_endian(Endian::Little)
2185        .flags(0x9f738b33685cc84c)
2186        .add_stream(SimpleStream {
2187            stream_type: 0x11223344,
2188            section,
2189        });
2190    assert_eq!(
2191        dump.finish().unwrap(),
2192        vec![
2193            0x4d,
2194            0x44,
2195            0x4d,
2196            0x50, // signature
2197            0x93,
2198            0xa7,
2199            0x00,
2200            0x00, // version
2201            1,
2202            0,
2203            0,
2204            0, // stream count
2205            directory_rva,
2206            0,
2207            0,
2208            0, // directory RVA
2209            0,
2210            0,
2211            0,
2212            0, // checksum
2213            0x3d,
2214            0xe1,
2215            0x44,
2216            0x4b, // time_date_stamp
2217            0x4c,
2218            0xc8,
2219            0x5c,
2220            0x68, // flags
2221            0x33,
2222            0x8b,
2223            0x73,
2224            0x9f,
2225            // Stream contents
2226            0x88,
2227            0x77,
2228            0x66,
2229            0x55,
2230            // Stream directory
2231            0x44,
2232            0x33,
2233            0x22,
2234            0x11, // stream type
2235            4,
2236            0,
2237            0,
2238            0, // size
2239            stream_rva,
2240            0,
2241            0,
2242            0, // rva
2243        ]
2244    );
2245}
2246
2247#[test]
2248fn test_simple_stream_bigendian() {
2249    let section = Section::with_endian(Endian::Big).D32(0x55667788);
2250    let stream_rva = mem::size_of::<md::MINIDUMP_HEADER>() as u8;
2251    let directory_rva = stream_rva + section.size() as u8;
2252    let dump = SynthMinidump::with_endian(Endian::Big)
2253        .flags(0x9f738b33685cc84c)
2254        .add_stream(SimpleStream {
2255            stream_type: 0x11223344,
2256            section,
2257        });
2258    assert_eq!(
2259        dump.finish().unwrap(),
2260        vec![
2261            0x50,
2262            0x4d,
2263            0x44,
2264            0x4d, // signature
2265            0x00,
2266            0x00,
2267            0xa7,
2268            0x93, // version
2269            0,
2270            0,
2271            0,
2272            1, // stream count
2273            0,
2274            0,
2275            0,
2276            directory_rva, // directory RVA
2277            0,
2278            0,
2279            0,
2280            0, // checksum
2281            0x4b,
2282            0x44,
2283            0xe1,
2284            0x3d, // time_date_stamp
2285            0x9f,
2286            0x73,
2287            0x8b,
2288            0x33, // flags
2289            0x68,
2290            0x5c,
2291            0xc8,
2292            0x4c,
2293            // Stream contents
2294            0x55,
2295            0x66,
2296            0x77,
2297            0x88,
2298            // Stream directory
2299            0x11,
2300            0x22,
2301            0x33,
2302            0x44, // stream type
2303            0,
2304            0,
2305            0,
2306            4, // size
2307            0,
2308            0,
2309            0,
2310            stream_rva, // rva
2311        ]
2312    );
2313}