1use crate::error::{self};
2use crate::options::Permissive;
3use alloc::string::ToString;
4use alloc::vec::Vec;
5use scroll::Pread;
6
7use super::options;
8use super::section_table;
9
10use crate::pe::data_directories::DataDirectory;
11use core::cmp;
12
13use log::debug;
14
15pub fn is_in_range(rva: usize, r1: usize, r2: usize) -> bool {
16 r1 <= rva && rva < r2
17}
18
19#[inline]
21fn aligned_pointer_to_raw_data(pointer_to_raw_data: usize) -> usize {
22 const PHYSICAL_ALIGN: usize = 0x1ff;
23 pointer_to_raw_data & !PHYSICAL_ALIGN
24}
25
26#[inline]
27fn section_read_size(section: §ion_table::SectionTable, file_alignment: u32) -> usize {
28 fn round_size(size: usize) -> usize {
29 const PAGE_MASK: usize = 0xfff;
30 (size + PAGE_MASK) & !PAGE_MASK
31 }
32
33 let file_alignment = file_alignment as usize;
56 let size_of_raw_data = section.size_of_raw_data as usize;
57 let virtual_size = section.virtual_size as usize;
58 let read_size = {
59 let read_size =
60 ((section.pointer_to_raw_data as usize + size_of_raw_data + file_alignment - 1)
61 & !(file_alignment - 1))
62 - aligned_pointer_to_raw_data(section.pointer_to_raw_data as usize);
63 cmp::min(read_size, round_size(size_of_raw_data))
64 };
65
66 if virtual_size == 0 {
67 read_size
68 } else if read_size == 0 {
69 virtual_size
70 } else {
71 cmp::min(read_size, round_size(virtual_size))
72 }
73}
74
75fn rva2offset(rva: usize, section: §ion_table::SectionTable) -> usize {
76 (section.pointer_to_raw_data as usize) + (rva - section.virtual_address as usize)
77}
78
79fn is_in_section(rva: usize, section: §ion_table::SectionTable, file_alignment: u32) -> bool {
80 let section_rva = section.virtual_address as usize;
81 is_in_range(
82 rva,
83 section_rva,
84 section_rva + section_read_size(section, file_alignment),
85 )
86}
87
88pub fn find_offset(
89 rva: usize,
90 sections: &[section_table::SectionTable],
91 file_alignment: u32,
92 opts: &options::ParseOptions,
93) -> Option<usize> {
94 if opts.resolve_rva {
95 if file_alignment == 0 || file_alignment & (file_alignment - 1) != 0 {
96 return None;
97 }
98 for (i, section) in sections.iter().enumerate() {
99 debug!(
100 "Checking {} for {:#x} ∈ {:#x}..{:#x}",
101 section.name().unwrap_or(""),
102 rva,
103 section.virtual_address,
104 section.virtual_address + section.virtual_size
105 );
106 if is_in_section(rva, §ion, file_alignment) {
107 let offset = rva2offset(rva, §ion);
108 debug!(
109 "Found in section {}({}), remapped into offset {:#x}",
110 section.name().unwrap_or(""),
111 i,
112 offset
113 );
114 return Some(offset);
115 }
116 }
117 None
118 } else {
119 Some(rva)
120 }
121}
122
123pub fn find_offset_or(
124 rva: usize,
125 sections: &[section_table::SectionTable],
126 file_alignment: u32,
127 opts: &options::ParseOptions,
128 msg: &str,
129) -> error::Result<usize> {
130 find_offset(rva, sections, file_alignment, opts)
131 .ok_or_else(|| error::Error::Malformed(msg.to_string()))
132}
133
134pub fn try_name<'a>(
135 bytes: &'a [u8],
136 rva: usize,
137 sections: &[section_table::SectionTable],
138 file_alignment: u32,
139 opts: &options::ParseOptions,
140) -> error::Result<&'a str> {
141 match find_offset(rva, sections, file_alignment, opts) {
142 Some(offset) => Ok(bytes.pread::<&str>(offset)?),
143 None => Err(error::Error::Malformed(format!(
144 "Cannot find name from rva {:#x} in sections: {:?}",
145 rva, sections
146 ))),
147 }
148}
149
150pub(crate) fn safe_try_name<'a>(
152 bytes: &'a [u8],
153 rva: usize,
154 sections: &[section_table::SectionTable],
155 file_alignment: u32,
156 opts: &options::ParseOptions,
157) -> error::Result<Option<&'a str>> {
158 match find_offset(rva, sections, file_alignment, opts) {
159 Some(offset) => {
160 if offset >= bytes.len() {
161 Err(error::Error::Malformed(format!(
162 "Name RVA {:#x} maps to offset {:#x} beyond file bounds (file size: {:#x}). \
163 This may indicate a packed binary.",
164 rva,
165 offset,
166 bytes.len()
167 )))
168 .or_permissive_and_default(
169 opts.parse_mode.is_permissive(),
170 "Name RVA maps beyond file bounds; treating as missing",
171 )
172 } else {
173 match bytes.pread::<&str>(offset) {
175 Ok(name) => Ok(Some(name)),
176 Err(e) => Err(error::Error::Malformed(format!(
177 "Failed to read name at offset {:#x} (RVA {:#x}): {}. \
178 This may indicate a packed binary.",
179 offset, rva, e
180 )))
181 .or_permissive_and_default(
182 opts.parse_mode.is_permissive(),
183 "Failed to read name; treating as missing",
184 ),
185 }
186 }
187 }
188 None => Err(error::Error::Malformed(format!(
189 "Cannot find name from rva {:#x} in sections: {:?}. \
190 This may be a packed binary or malformed sections.",
191 rva, sections
192 )))
193 .or_permissive_and_default(
194 opts.parse_mode.is_permissive(),
195 "Cannot map RVA to name; treating as missing",
196 ),
197 }
198}
199
200pub fn get_data<'a, T>(
201 bytes: &'a [u8],
202 sections: &[section_table::SectionTable],
203 directory: DataDirectory,
204 file_alignment: u32,
205) -> error::Result<T>
206where
207 T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>,
208{
209 get_data_with_opts(
210 bytes,
211 sections,
212 directory,
213 file_alignment,
214 &options::ParseOptions::default(),
215 )
216}
217
218pub fn get_data_with_opts<'a, T>(
219 bytes: &'a [u8],
220 sections: &[section_table::SectionTable],
221 directory: DataDirectory,
222 file_alignment: u32,
223 opts: &options::ParseOptions,
224) -> error::Result<T>
225where
226 T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>,
227{
228 let rva = directory.virtual_address as usize;
229 let offset = find_offset(rva, sections, file_alignment, opts)
230 .ok_or_else(|| error::Error::Malformed(directory.virtual_address.to_string()))?;
231 let result: T = bytes.pread_with(offset, scroll::LE)?;
232 Ok(result)
233}
234
235pub(crate) fn pad(length: usize, alignment: Option<usize>) -> Option<Vec<u8>> {
236 match alignment {
237 Some(alignment) => {
238 let overhang = length % alignment;
239 if overhang != 0 {
240 let repeat = alignment - overhang;
241 Some(vec![0u8; repeat])
242 } else {
243 None
244 }
245 }
246 None => None,
247 }
248}
249
250#[inline]
252pub(crate) fn align_up<N>(value: N, align: N) -> N
253where
254 N: core::ops::Add<Output = N>
255 + core::ops::Not<Output = N>
256 + core::ops::BitAnd<Output = N>
257 + core::ops::Sub<Output = N>
258 + core::cmp::PartialEq
259 + core::marker::Copy,
260 u8: Into<N>,
261{
262 debug_assert!(align != 0u8.into(), "Align must be non-zero");
263 (value + align - 1u8.into()) & !(align - 1u8.into())
264}