1#![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
26pub struct SynthMinidump {
28 section: Section,
30 flags: Label,
32 stream_count: u32,
34 stream_count_label: Label,
36 stream_directory_rva: Label,
38 stream_directory: Section,
40 system_info: Option<SystemInfo>,
42 exception: Option<Exception>,
44 module_list: Option<ListStream<Module>>,
46 unloaded_module_list: Option<ExListStream<UnloadedModule>>,
48 thread_list: Option<ListStream<Thread>>,
50 thread_names_list: Option<ListStream<ThreadName>>,
52 memory_list: Option<ListStream<Section>>,
54 memory64_list: Option<Memory64ListStream>,
56 memory_info_list: Option<ExListStream<MemoryInfo>>,
58 crashpad_info: Option<CrashpadInfo>,
60 linux_maps: Option<SimpleStream>,
62 linux_lsb_release: Option<SimpleStream>,
64 linux_cpu_info: Option<SimpleStream>,
66 linux_environ: Option<SimpleStream>,
68 linux_proc_status: Option<SimpleStream>,
70 linux_proc_limits: Option<SimpleStream>,
72 soft_errors: Option<SimpleStream>,
74 memory64_section: Option<Section>,
76 handle_data_stream: Option<ExListStream<HandleDescriptor>>,
78}
79
80pub trait DumpSection {
82 fn file_offset(&self) -> Label;
84
85 fn file_size(&self) -> Label;
87}
88
89pub trait ListItem: DumpSection {
98 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 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 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
138pub trait SectionExtra {
140 fn cite_location<T: CiteLocation>(self, thing: &T) -> Self;
142 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
155pub trait Stream: DumpSection + Into<Section> {
157 fn stream_type(&self) -> u32;
159 fn cite_stream_in(&self, section: Section) -> Section {
161 section.D32(self.stream_type()).cite_location(self)
162 }
163}
164
165impl SynthMinidump {
166 pub fn new() -> SynthMinidump {
168 SynthMinidump::with_endian(DEFAULT_ENDIAN)
169 }
170
171 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) .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 pub fn flags(self, flags: u64) -> SynthMinidump {
247 self.flags.set_const(flags);
248 self
249 }
250
251 #[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 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 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 pub fn add_memory(mut self, memory: Memory) -> SynthMinidump {
283 let descriptor = memory.cite_memory_in(Section::with_endian(self.section.endian));
285 self.memory_list = self
287 .memory_list
288 .take()
289 .map(|memory_list| memory_list.add(descriptor));
290 self.add(memory)
292 }
293
294 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 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 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 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 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 pub fn add_crashpad_info(mut self, crashpad_info: CrashpadInfo) -> Self {
345 self.crashpad_info = Some(crashpad_info);
346 self
347 }
348
349 pub fn add_system_info(mut self, system_info: SystemInfo) -> Self {
351 self.system_info = Some(system_info);
352 self
353 }
354
355 pub fn add_exception(mut self, exception: Exception) -> Self {
357 self.exception = Some(exception);
358 self
359 }
360
361 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 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 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 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 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 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 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 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 pub fn finish(mut self) -> Option<Vec<u8>> {
459 let modules = self.module_list.take();
461 self = self.finish_list(modules);
462 let unloaded_modules = self.unloaded_module_list.take();
464 self = self.finish_ex_list(unloaded_modules);
465 let memories = self.memory_list.take();
467 self = self.finish_list(memories);
468 if let Some(memories64) = self.memory64_list.take() {
470 if !memories64.is_empty() {
471 self = self.add_stream(memories64);
472 }
473 }
474 let memory_infos = self.memory_info_list.take();
476 self = self.finish_ex_list(memory_infos);
477 let threads = self.thread_list.take();
479 self = self.finish_list(threads);
480 let thread_names = self.thread_names_list.take();
482 self = self.finish_list(thread_names);
483 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 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 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
571pub struct SimpleStream {
573 pub stream_type: u32,
575 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
593pub struct List<T: ListItem> {
595 section: Section,
597 count: u32,
599 count_label: Label,
601 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 #[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(§ion.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 list.count_label.set_const(list.count as u64);
649
650 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 stream_type: u32,
670 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 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
769pub struct ExList<T: ListItem> {
771 section: Section,
773 count: u32,
775 count_label: Label,
777 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 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 #[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(§ion.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 list.count_label.set_const(list.count as u64);
850
851 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 stream_type: u32,
871 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
928pub struct DumpString {
930 section: Section,
931}
932
933impl DumpString {
934 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
990pub 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
1007pub 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 .D64(0)
1077 .D64(0)
1079 }
1080}
1081
1082pub 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
1115pub 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) .D32(0) .D32(0) .D64(0) .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
1145pub 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 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
1171pub struct Memory {
1173 section: Section,
1174 pub address: u64,
1175}
1176
1177impl Memory {
1178 pub fn with_section(section: Section, address: u64) -> Memory {
1181 Memory { section, address }
1182 }
1183
1184 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
1198pub 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) .D64(region_size)
1221 .D32(state)
1222 .D32(protection)
1223 .D32(ty)
1224 .D32(0); 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
1237pub 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
1276pub struct MiscStream {
1281 section: Section,
1283
1284 pub process_id: Option<u32>,
1286 pub process_times: Option<MiscFieldsProcessTimes>,
1288
1289 pub power_info: Option<MiscFieldsPowerInfo>,
1291
1292 pub process_integrity_level: Option<u32>,
1294 pub process_execute_flags: Option<u32>,
1296 pub protected_process: Option<u32>,
1298 pub time_zone: Option<MiscFieldsTimeZone>,
1300
1301 pub build_strings: Option<MiscFieldsBuildString>,
1303
1304 pub misc_5: Option<MiscInfo5Fields>,
1306
1307 pub pad_to_size: Option<usize>,
1308}
1309
1310#[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#[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#[derive(Default)]
1330pub struct MiscFieldsTimeZone {
1331 pub time_zone_id: u32,
1332 pub time_zone: md::TIME_ZONE_INFORMATION,
1333}
1334
1335pub 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#[derive(Default)]
1352pub struct MiscInfo5Fields {
1353 pub xstate_data: md::XSTATE_CONFIG_FEATURE_MSC_INFO,
1354 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 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 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 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
1547pub fn x86_context(endian: Endian, eip: u32, esp: u32) -> Section {
1549 let section = Section::with_endian(endian)
1550 .D32(0x1003f) .append_repeated(0, 4 * 6) .append_repeated(0, md::FLOATING_SAVE_AREA_X86::size_with(&LE)) .append_repeated(0, 4 * 11) .D32(eip)
1555 .D32(0) .D32(0) .D32(esp)
1558 .D32(0) .append_repeated(0, 512); assert_eq!(section.size(), md::CONTEXT_X86::size_with(&LE) as u64);
1561 section
1562}
1563
1564pub 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) .D32(0x10001f) .D32(0) .append_repeated(0, mem::size_of::<u16>() * 6) .D32(0) .append_repeated(0, mem::size_of::<u64>() * 6) .append_repeated(0, mem::size_of::<u64>() * 4) .D64(rsp)
1575 .append_repeated(0, mem::size_of::<u64>() * 11) .D64(rip)
1577 .append_repeated(0, 512) .append_repeated(0, mem::size_of::<u128>() * 26) .append_repeated(0, mem::size_of::<u64>() * 6); assert_eq!(section.size(), md::CONTEXT_AMD64::size_with(&LE) as u64);
1581 section
1582}
1583
1584pub fn arm64_context(endian: Endian, pc: u64, sp: u64) -> Section {
1586 let section = Section::with_endian(endian)
1587 .D32(0x40001f) .D32(0) .append_repeated(0, mem::size_of::<u64>() * 31) .D64(sp) .D64(pc) .append_repeated(0, mem::size_of::<u128>() * 32) .D32(0) .D32(0) .append_repeated(0, mem::size_of::<u32>() * 8) .append_repeated(0, mem::size_of::<u64>() * 8) .append_repeated(0, mem::size_of::<u32>() * 2) .append_repeated(0, mem::size_of::<u64>() * 2); 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); 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
1726pub 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
1816impl 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
1890pub 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 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 pub exception_record: ExceptionRecord,
2001 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 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) .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); 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, 0x93, 0xa7, 0x00, 0x00, 0, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0x3d, 0xe1, 0x44, 0x4b, 0x4c, 0xc8, 0x5c, 0x68, 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, 0x00, 0x00, 0xa7, 0x93, 0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0, 0, 0x4b, 0x44, 0xe1, 0x3d, 0x9f, 0x73, 0x8b, 0x33, 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 assert_eq!(
2122 &contents[mem::size_of::<md::MINIDUMP_HEADER>()..],
2123 &[
2124 0xa, 0x0, 0x0, 0x0, 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, 12, 0, 0, 0, 20, 0, 0, 0, 3, 0, 0, 0, 102, 111, 111, 0, 3, 0, 0, 0, 98, 97, 114, 0 ]
2152 );
2153}
2154
2155#[test]
2156fn test_list_stream() {
2157 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, 0x2, 0x0, 0x0, 0x0, b'a', 0x0, 0x2, 0x0, 0x0, 0x0, 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, 0x93,
2198 0xa7,
2199 0x00,
2200 0x00, 1,
2202 0,
2203 0,
2204 0, directory_rva,
2206 0,
2207 0,
2208 0, 0,
2210 0,
2211 0,
2212 0, 0x3d,
2214 0xe1,
2215 0x44,
2216 0x4b, 0x4c,
2218 0xc8,
2219 0x5c,
2220 0x68, 0x33,
2222 0x8b,
2223 0x73,
2224 0x9f,
2225 0x88,
2227 0x77,
2228 0x66,
2229 0x55,
2230 0x44,
2232 0x33,
2233 0x22,
2234 0x11, 4,
2236 0,
2237 0,
2238 0, stream_rva,
2240 0,
2241 0,
2242 0, ]
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, 0x00,
2266 0x00,
2267 0xa7,
2268 0x93, 0,
2270 0,
2271 0,
2272 1, 0,
2274 0,
2275 0,
2276 directory_rva, 0,
2278 0,
2279 0,
2280 0, 0x4b,
2282 0x44,
2283 0xe1,
2284 0x3d, 0x9f,
2286 0x73,
2287 0x8b,
2288 0x33, 0x68,
2290 0x5c,
2291 0xc8,
2292 0x4c,
2293 0x55,
2295 0x66,
2296 0x77,
2297 0x88,
2298 0x11,
2300 0x22,
2301 0x33,
2302 0x44, 0,
2304 0,
2305 0,
2306 4, 0,
2308 0,
2309 0,
2310 stream_rva, ]
2312 );
2313}