Bug 1689358 - Generate minidumps for child process crashes using the minidump-writer...
[gecko.git] / third_party / rust / goblin / src / pe / utils.rs
blob07a320d57b240992a6b95327684f67ed3164d4d4
1 use crate::error;
2 use alloc::string::ToString;
3 use scroll::Pread;
5 use super::options;
6 use super::section_table;
8 use crate::pe::data_directories::DataDirectory;
9 use core::cmp;
11 use log::debug;
13 pub fn is_in_range(rva: usize, r1: usize, r2: usize) -> bool {
14     r1 <= rva && rva < r2
17 // reference: Peter Ferrie. Reliable algorithm to extract overlay of a PE. https://bit.ly/2vBX2bR
18 #[inline]
19 fn aligned_pointer_to_raw_data(pointer_to_raw_data: usize) -> usize {
20     const PHYSICAL_ALIGN: usize = 0x1ff;
21     pointer_to_raw_data & !PHYSICAL_ALIGN
24 #[inline]
25 fn section_read_size(section: &section_table::SectionTable, file_alignment: u32) -> usize {
26     fn round_size(size: usize) -> usize {
27         const PAGE_MASK: usize = 0xfff;
28         (size + PAGE_MASK) & !PAGE_MASK
29     }
31     // Paraphrased from https://reverseengineering.stackexchange.com/a/4326 (by Peter Ferrie).
32     //
33     // Handles the corner cases such as mis-aligned pointers (round down) and sizes (round up)
34     // Further rounding corner cases:
35     // - the physical pointer should be rounded down to a multiple of 512, regardless of the value in the header
36     // - the read size is rounded up by using a combination of the file alignment and 4kb
37     // - the virtual size is always rounded up to a multiple of 4kb, regardless of the value in the header.
38     //
39     // Reference C implementation:
40     //
41     // long pointerToRaw = section.get(POINTER_TO_RAW_DATA);
42     // long alignedpointerToRaw = pointerToRaw & ~0x1ff;
43     // long sizeOfRaw = section.get(SIZE_OF_RAW_DATA);
44     // long readsize = ((pointerToRaw + sizeOfRaw) + filealign - 1) & ~(filealign - 1)) - alignedpointerToRaw;
45     // readsize = min(readsize, (sizeOfRaw + 0xfff) & ~0xfff);
46     // long virtsize = section.get(VIRTUAL_SIZE);
47     //
48     // if (virtsize)
49     // {
50     //     readsize = min(readsize, (virtsize + 0xfff) & ~0xfff);
51     // }
53     let file_alignment = file_alignment as usize;
54     let size_of_raw_data = section.size_of_raw_data as usize;
55     let virtual_size = section.virtual_size as usize;
56     let read_size = {
57         let read_size =
58             ((section.pointer_to_raw_data as usize + size_of_raw_data + file_alignment - 1)
59                 & !(file_alignment - 1))
60                 - aligned_pointer_to_raw_data(section.pointer_to_raw_data as usize);
61         cmp::min(read_size, round_size(size_of_raw_data))
62     };
64     if virtual_size == 0 {
65         read_size
66     } else {
67         cmp::min(read_size, round_size(virtual_size))
68     }
71 fn rva2offset(rva: usize, section: &section_table::SectionTable) -> usize {
72     (rva - section.virtual_address as usize)
73         + aligned_pointer_to_raw_data(section.pointer_to_raw_data as usize)
76 fn is_in_section(rva: usize, section: &section_table::SectionTable, file_alignment: u32) -> bool {
77     let section_rva = section.virtual_address as usize;
78     is_in_range(
79         rva,
80         section_rva,
81         section_rva + section_read_size(section, file_alignment),
82     )
85 pub fn find_offset(
86     rva: usize,
87     sections: &[section_table::SectionTable],
88     file_alignment: u32,
89     opts: &options::ParseOptions,
90 ) -> Option<usize> {
91     if opts.resolve_rva {
92         if file_alignment == 0 || file_alignment & (file_alignment - 1) != 0 {
93             return None;
94         }
95         for (i, section) in sections.iter().enumerate() {
96             debug!(
97                 "Checking {} for {:#x} ∈ {:#x}..{:#x}",
98                 section.name().unwrap_or(""),
99                 rva,
100                 section.virtual_address,
101                 section.virtual_address + section.virtual_size
102             );
103             if is_in_section(rva, &section, file_alignment) {
104                 let offset = rva2offset(rva, &section);
105                 debug!(
106                     "Found in section {}({}), remapped into offset {:#x}",
107                     section.name().unwrap_or(""),
108                     i,
109                     offset
110                 );
111                 return Some(offset);
112             }
113         }
114         None
115     } else {
116         Some(rva)
117     }
120 pub fn find_offset_or(
121     rva: usize,
122     sections: &[section_table::SectionTable],
123     file_alignment: u32,
124     opts: &options::ParseOptions,
125     msg: &str,
126 ) -> error::Result<usize> {
127     find_offset(rva, sections, file_alignment, opts)
128         .ok_or_else(|| error::Error::Malformed(msg.to_string()))
131 pub fn try_name<'a>(
132     bytes: &'a [u8],
133     rva: usize,
134     sections: &[section_table::SectionTable],
135     file_alignment: u32,
136     opts: &options::ParseOptions,
137 ) -> error::Result<&'a str> {
138     match find_offset(rva, sections, file_alignment, opts) {
139         Some(offset) => Ok(bytes.pread::<&str>(offset)?),
140         None => Err(error::Error::Malformed(format!(
141             "Cannot find name from rva {:#x} in sections: {:?}",
142             rva, sections
143         ))),
144     }
147 pub fn get_data<'a, T>(
148     bytes: &'a [u8],
149     sections: &[section_table::SectionTable],
150     directory: DataDirectory,
151     file_alignment: u32,
152 ) -> error::Result<T>
153 where
154     T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>,
156     get_data_with_opts(
157         bytes,
158         sections,
159         directory,
160         file_alignment,
161         &options::ParseOptions::default(),
162     )
165 pub fn get_data_with_opts<'a, T>(
166     bytes: &'a [u8],
167     sections: &[section_table::SectionTable],
168     directory: DataDirectory,
169     file_alignment: u32,
170     opts: &options::ParseOptions,
171 ) -> error::Result<T>
172 where
173     T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>,
175     let rva = directory.virtual_address as usize;
176     let offset = find_offset(rva, sections, file_alignment, opts)
177         .ok_or_else(|| error::Error::Malformed(directory.virtual_address.to_string()))?;
178     let result: T = bytes.pread_with(offset, scroll::LE)?;
179     Ok(result)