pelite\pe64/
exception.rs

1/*!
2Exception Directory.
3*/
4
5use std::{fmt, iter, mem, slice};
6use std::cmp::Ordering;
7
8use crate::{Error, Result};
9
10use super::image::*;
11use super::Pe;
12
13//----------------------------------------------------------------
14
15/// Exception Directory.
16///
17/// For more information see the [module-level documentation](index.html).
18#[derive(Copy, Clone)]
19pub struct Exception<'a, P> {
20	pe: P,
21	image: &'a [RUNTIME_FUNCTION],
22}
23impl<'a, P: Pe<'a>> Exception<'a, P> {
24	pub(crate) fn try_from(pe: P) -> Result<Exception<'a, P>> {
25		let datadir = pe.data_directory().get(IMAGE_DIRECTORY_ENTRY_EXCEPTION).ok_or(Error::Bounds)?;
26		let (len, rem) = (
27			datadir.Size as usize / mem::size_of::<RUNTIME_FUNCTION>(),
28			datadir.Size as usize % mem::size_of::<RUNTIME_FUNCTION>(),
29		);
30		if rem != 0 {
31			return Err(Error::Invalid);
32		}
33		let image = pe.derva_slice(datadir.VirtualAddress, len)?;
34		Ok(Exception { pe, image })
35	}
36	/// Gets the PE instance.
37	pub fn pe(&self) -> P {
38		self.pe
39	}
40	/// Returns the functions slice.
41	pub fn image(&self) -> &'a [RUNTIME_FUNCTION] {
42		self.image
43	}
44	/// Checks if the function table is sorted.
45	///
46	/// The PE specification says that the list of runtime functions should be sorted to allow binary search.
47	/// This function checks if the runtime functions are actually sorted, if not then lookups may fail unexpectedly.
48	pub fn check_sorted(&self) -> bool {
49		self.image.windows(2).all(|window|
50			window[0].BeginAddress <= window[0].EndAddress &&
51			window[0].EndAddress <= window[1].BeginAddress &&
52			window[1].BeginAddress <= window[1].EndAddress
53		)
54	}
55	/// Gets an iterator over the function records.
56	pub fn functions(&self)
57		-> iter::Map<slice::Iter<'a, RUNTIME_FUNCTION>, impl Clone + FnMut(&'a RUNTIME_FUNCTION) -> Function<'a, P>>
58	{
59		let pe = self.pe;
60		self.image.iter()
61			.map(move |image| Function { pe, image })
62	}
63	/// Finds the index of the function for the given program counter.
64	pub fn index_of(&self, pc: Rva) -> std::result::Result<usize, usize> {
65		self.image.binary_search_by(|rf| {
66			if pc < rf.BeginAddress {
67				Ordering::Less
68			}
69			else if pc > rf.EndAddress {
70				Ordering::Greater
71			}
72			else {
73				Ordering::Equal
74			}
75		})
76	}
77	/// Finds the function for the given 'program counter' address.
78	///
79	/// The function records are sorted by their address allowing binary search for the record.
80	pub fn lookup_function_entry(&self, pc: Rva) -> Option<Function<'a, P>> {
81		self.index_of(pc).map(|index| Function {
82			pe: self.pe,
83			image: &self.image[index]
84		}).ok()
85	}
86}
87impl<'a, P: Pe<'a>> fmt::Debug for Exception<'a, P> {
88	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89		f.debug_struct("Exception")
90			.field("functions.len", &self.image.len())
91			.finish()
92	}
93}
94
95//----------------------------------------------------------------
96
97/// Runtime function.
98#[derive(Copy, Clone)]
99pub struct Function<'a, P> {
100	pe: P,
101	image: &'a RUNTIME_FUNCTION,
102}
103impl<'a, P: Pe<'a>> Function<'a, P> {
104	/// Gets the PE instance.
105	pub fn pe(&self) -> P {
106		self.pe
107	}
108	/// Returns the underlying runtime function image.
109	pub fn image(&self) -> &'a RUNTIME_FUNCTION {
110		self.image
111	}
112	/// Gets the function bytes.
113	pub fn bytes(&self) -> Result<&'a [u8]> {
114		let len = if self.image.BeginAddress > self.image.EndAddress { return Err(Error::Overflow); }
115		else { (self.image.EndAddress - self.image.BeginAddress) as usize };
116		self.pe.derva_slice(self.image.BeginAddress, len)
117	}
118	/// Gets the unwind info.
119	pub fn unwind_info(&self) -> Result<UnwindInfo<'a, P>> {
120		// Read as many bytes as we can for interpretation
121		let bytes = self.pe.slice(
122			self.image.UnwindData,
123			mem::size_of::<UNWIND_INFO>(),
124			if cfg!(feature = "unsafe_alignment") { 1 } else { mem::align_of::<UNWIND_INFO>() }
125		)?;
126		let image = unsafe { &*(bytes.as_ptr() as *const UNWIND_INFO) };
127		// Calculate actual size including size of unwind codes
128		let min_size_of = mem::size_of::<UNWIND_INFO>() +
129			mem::size_of::<UNWIND_CODE>() * image.CountOfCodes as usize;
130		if bytes.len() < min_size_of {
131			return Err(Error::Bounds);
132		}
133		// Ok
134		Ok(UnwindInfo { pe: self.pe, image })
135	}
136}
137impl<'a, P: Pe<'a>> fmt::Debug for Function<'a, P> {
138	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139		f.debug_struct("Function")
140			.field("bytes.len", &self.bytes().map(<[_]>::len))
141			.finish()
142	}
143}
144
145//----------------------------------------------------------------
146
147/// Unwind info.
148#[derive(Copy, Clone)]
149pub struct UnwindInfo<'a, P> {
150	pe: P,
151	image: &'a UNWIND_INFO,
152}
153impl<'a, P: Pe<'a>> UnwindInfo<'a, P> {
154	/// Gets the PE instance.
155	pub fn pe(&self) -> P {
156		self.pe
157	}
158	/// Returns the underlying unwind info image.
159	pub fn image(&self) -> &'a UNWIND_INFO {
160		self.image
161	}
162	pub fn version(&self) -> u8 {
163		self.image.VersionFlags & 0b00000111
164	}
165	pub fn flags(&self) -> u8 {
166		self.image.VersionFlags >> 3
167	}
168	pub fn size_of_prolog(&self) -> usize {
169		self.image.SizeOfProlog as usize
170	}
171	pub fn frame_register(&self) -> u8 {
172		self.image.FrameRegisterOffset & 0b00001111
173	}
174	pub fn frame_offset(&self) -> u8 {
175		self.image.FrameRegisterOffset >> 4
176	}
177	pub fn unwind_codes(&self) -> &'a [UNWIND_CODE] {
178		let len = self.image.CountOfCodes as usize;
179		unsafe {
180			slice::from_raw_parts(self.image.UnwindCode.as_ptr(), len)
181		}
182	}
183}
184impl<'a, P: Pe<'a>> fmt::Debug for UnwindInfo<'a, P> {
185	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186		f.debug_struct("UnwindInfo")
187			.field("version", &self.version())
188			.field("flags", &self.flags())
189			.field("size_of_prolog", &self.size_of_prolog())
190			.field("frame_register", &self.frame_register())
191			.field("frame_offset", &self.frame_offset())
192			.field("unwind_codes.len", &self.unwind_codes().len())
193			.finish()
194	}
195}
196
197//----------------------------------------------------------------
198
199#[cfg(test)]
200pub(crate) fn test<'a, P: Pe<'a>>(pe: P) -> Result<()> {
201	let exception = pe.exception()?;
202	let _ = format!("{:?}", exception);
203
204	let sorted = exception.check_sorted();
205
206	for (index, function) in exception.functions().enumerate() {
207		let _ = format!("{:?}", function);
208		let _bytes = function.bytes();
209
210		if sorted {
211			for pc in function.image().BeginAddress..function.image().EndAddress {
212				assert_eq!(exception.index_of(pc), Ok(index));
213			}
214		}
215
216		if let Ok(unwind_info) = function.unwind_info() {
217			let _ = format!("{:?}", unwind_info);
218			let _version = unwind_info.version();
219			let _flags = unwind_info.flags();
220			let _size_of_prolog = unwind_info.size_of_prolog();
221			let _frame_register = unwind_info.frame_register();
222			let _frame_offset = unwind_info.frame_offset();
223			let _unwind_codes = unwind_info.unwind_codes();
224		}
225	}
226
227	Ok(())
228}