pelite/pe64/
debug.rs

1/*!
2Debug Directory.
3
4# Examples
5
6```
7# #![allow(unused_variables)]
8use pelite::pe64::{Pe, PeFile, debug};
9
10# #[allow(dead_code)]
11fn example(file: PeFile<'_>) -> pelite::Result<()> {
12	// Access the debug directory
13	let debug = file.debug()?;
14
15	// Get the CodeView PDB file name
16	if let Some(pdb_file_name) = debug.pdb_file_name() {
17		println!("PDB: {}", pdb_file_name);
18	}
19
20	Ok(())
21}
22```
23*/
24
25use std::{fmt, iter, mem, slice};
26
27use crate::{util::CStr};
28use crate::{Error, Result};
29use crate::util::AlignTo;
30
31use super::{Align, Pe, image::*};
32
33//----------------------------------------------------------------
34
35/// Debug directory.
36///
37/// For more information see the [module-level documentation](index.html).
38#[derive(Copy, Clone)]
39pub struct Debug<'a, P> {
40	pe: P,
41	image: &'a [IMAGE_DEBUG_DIRECTORY],
42}
43impl<'a, P: Pe<'a>> Debug<'a, P> {
44	pub(crate) fn try_from(pe: P) -> Result<Debug<'a, P>> {
45		let datadir = pe.data_directory().get(IMAGE_DIRECTORY_ENTRY_DEBUG).ok_or(Error::Bounds)?;
46		let (len, rem) = (
47			datadir.Size as usize / mem::size_of::<IMAGE_DEBUG_DIRECTORY>(),
48			datadir.Size as usize % mem::size_of::<IMAGE_DEBUG_DIRECTORY>(),
49		);
50		if rem != 0 {
51			return Err(Error::Invalid);
52		}
53		let image = pe.derva_slice(datadir.VirtualAddress, len)?;
54		Ok(Debug { pe, image })
55	}
56	/// Gets the PE instance.
57	pub fn pe(&self) -> P {
58		self.pe
59	}
60	/// Returns the underlying debug directories image.
61	pub fn image(&self) -> &'a [IMAGE_DEBUG_DIRECTORY] {
62		self.image
63	}
64	/// Gets the CodeView PDB file name.
65	pub fn pdb_file_name(&self) -> Option<&'a CStr> {
66		self.into_iter()
67			.filter_map(|dir| dir.entry().ok().and_then(Entry::as_code_view).map(|cv| cv.pdb_file_name()))
68			.next()
69	}
70	/// Iterator over the debug directories.
71	pub fn iter(&self) -> Iter<'a, P> {
72		Iter {
73			pe: self.pe,
74			iter: self.image.iter()
75		}
76	}
77}
78impl<'a, P: Pe<'a>> IntoIterator for Debug<'a, P> {
79	type Item = Dir<'a, P>;
80	type IntoIter = Iter<'a, P>;
81	fn into_iter(self) -> Iter<'a, P> {
82		self.iter()
83	}
84}
85impl<'a, P: Pe<'a>> fmt::Debug for Debug<'a, P> {
86	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
87		f.debug_list().entries(*self).finish()
88	}
89}
90
91//----------------------------------------------------------------
92
93/// Iterator over Dir entries.
94#[derive(Clone)]
95pub struct Iter<'a, P> {
96	pe: P,
97	iter: slice::Iter<'a, IMAGE_DEBUG_DIRECTORY>,
98}
99impl<'a, P: Pe<'a>> Iter<'a, P> {
100	pub fn image(&self) -> &'a [IMAGE_DEBUG_DIRECTORY] {
101		self.iter.as_slice()
102	}
103}
104impl<'a, P: Pe<'a>> Iterator for Iter<'a, P> {
105	type Item = Dir<'a, P>;
106	fn next(&mut self) -> Option<Dir<'a, P>> {
107		self.iter.next().map(|image| Dir { pe: self.pe, image })
108	}
109	fn size_hint(&self) -> (usize, Option<usize>) {
110		self.iter.size_hint()
111	}
112	fn count(self) -> usize {
113		self.iter.count()
114	}
115	fn nth(&mut self, n: usize) -> Option<Dir<'a, P>> {
116		self.iter.nth(n).map(|image| Dir { pe: self.pe, image })
117	}
118}
119impl<'a, P: Pe<'a>> DoubleEndedIterator for Iter<'a, P> {
120	fn next_back(&mut self) -> Option<Dir<'a, P>> {
121		self.iter.next_back().map(|image| Dir { pe: self.pe, image })
122	}
123}
124impl<'a, P: Pe<'a>> ExactSizeIterator for Iter<'a, P> {}
125impl<'a, P: Pe<'a>> iter::FusedIterator for Iter<'a, P> {}
126
127//----------------------------------------------------------------
128
129/// Debug directory entry.
130#[derive(Copy, Clone)]
131pub struct Dir<'a, P> {
132	pe: P,
133	image: &'a IMAGE_DEBUG_DIRECTORY,
134}
135impl<'a, P: Pe<'a>> Dir<'a, P> {
136	/// Gets the PE instance.
137	pub fn pe(&self) -> P {
138		self.pe
139	}
140	/// Gets the underlying debug directory image.
141	pub fn image(&self) -> &'a IMAGE_DEBUG_DIRECTORY {
142		self.image
143	}
144	/// Gets the raw data of this debug directory entry.
145	pub fn data(&self) -> Option<&'a [u8]> {
146		let image = self.pe.image();
147		let size = self.image.SizeOfData as usize;
148		let offset = match self.pe.align() {
149			Align::File => self.image.PointerToRawData,
150			Align::Section => self.image.AddressOfRawData,
151		} as usize;
152		image.get(offset..offset.wrapping_add(size))
153	}
154	/// Interprets the directory entry.
155	pub fn entry(&self) -> Result<Entry<'a>> {
156		match self.image.Type {
157			IMAGE_DEBUG_TYPE_CODEVIEW => Ok(Entry::CodeView(code_view(&self)?)),
158			IMAGE_DEBUG_TYPE_MISC => Ok(Entry::Dbg(dbg(&self)?)),
159			IMAGE_DEBUG_TYPE_POGO => Ok(Entry::Pgo(pgo(&self)?)),
160			_ => Ok(Entry::Unknown(self.data()))
161		}
162	}
163}
164impl<'a, P: Pe<'a>> fmt::Debug for Dir<'a, P> {
165	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
166		f.debug_struct("Dir")
167			.field("type", &crate::stringify::DebugType(self.image.Type).to_str().ok_or(self.image.Type))
168			.field("time_date_stamp", &self.image.TimeDateStamp)
169			.field("version", &self.image.Version)
170			.field("entry", &self.entry())
171			.finish()
172	}
173}
174
175//----------------------------------------------------------------
176
177pub use crate::wrap::debug::{Entry, CodeView, Dbg, Pgo, PgoIter, PgoItem};
178
179fn code_view<'a, P: Pe<'a>>(dir: &Dir<'a, P>) -> Result<CodeView<'a>> {
180	let bytes = dir.data().ok_or(Error::Bounds)?;
181	if bytes.len() < 16 {
182		return Err(Error::Bounds);
183	}
184	if !(cfg!(feature = "unsafe_alignment") || bytes.as_ptr().aligned_to(4)) {
185		return Err(Error::Misaligned);
186	}
187	let cv_signature = unsafe { &*(bytes.as_ptr() as *const [u8; 4]) };
188	match cv_signature {
189		b"NB10" => {
190			if bytes.len() < 16 {
191				return Err(Error::Bounds);
192			}
193			let image = unsafe { &*(bytes.as_ptr() as *const IMAGE_DEBUG_CV_INFO_PDB20) };
194			let pdb_file_name = CStr::from_bytes(&bytes[16..]).ok_or(Error::Encoding)?;
195			Ok(CodeView::Cv20 { image, pdb_file_name })
196		},
197		b"RSDS" => {
198			if bytes.len() < 24 {
199				return Err(Error::Bounds);
200			}
201			let image = unsafe { &*(bytes.as_ptr() as *const IMAGE_DEBUG_CV_INFO_PDB70) };
202			let pdb_file_name = CStr::from_bytes(&bytes[24..]).ok_or(Error::Encoding)?;
203			Ok(CodeView::Cv70 { image, pdb_file_name })
204		},
205		_ => Err(Error::BadMagic),
206	}
207}
208
209fn dbg<'a, P: Pe<'a>>(dir: &Dir<'a, P>) -> Result<Dbg<'a>> {
210	let data = dir.data().ok_or(Error::Bounds)?;
211	if data.len() < mem::size_of::<IMAGE_DEBUG_MISC>() {
212		return Err(Error::Bounds);
213	}
214	if !(cfg!(feature = "unsafe_alignment") || data.as_ptr().aligned_to(4)) {
215		return Err(Error::Misaligned);
216	}
217	let image = unsafe { &*(data.as_ptr() as *const IMAGE_DEBUG_MISC) };
218	Ok(Dbg { image })
219}
220
221fn pgo<'a, P: Pe<'a>>(dir: &Dir<'a, P>) -> Result<Pgo<'a>> {
222	let data = dir.data().ok_or(Error::Bounds)?;
223	if data.len() < 4 {
224		return Err(Error::Bounds);
225	}
226	if !(cfg!(feature = "unsafe_alignment") || data.as_ptr().aligned_to(4)) {
227		return Err(Error::Misaligned);
228	}
229	let len = data.len() / 4;
230	let image = unsafe { slice::from_raw_parts(data.as_ptr() as *const u32, len) };
231	Ok(Pgo { image })
232}
233
234//----------------------------------------------------------------
235
236/*
237	"debug": [
238		{
239			"type": "CodeView",
240			"time_date_stamp": 0,
241			"version": "1.0",
242			"entry": {
243				format: "RSDS",
244				pdb_file_name: "",
245				...
246			},
247		},
248	],
249*/
250
251#[cfg(feature = "serde")]
252mod serde {
253	use crate::util::serde_helper::*;
254	use super::{Pe, Debug, Dir};
255
256	impl<'a, P: Pe<'a>> Serialize for Debug<'a, P> {
257		fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
258			serializer.collect_seq(self.into_iter())
259		}
260	}
261	impl<'a, P: Pe<'a>> Serialize for Dir<'a, P> {
262		fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
263			let is_human_readable = serializer.is_human_readable();
264			let mut state = serializer.serialize_struct("Dir", 4)?;
265			if is_human_readable {
266				state.serialize_field("type", &crate::stringify::DebugType(self.image.Type).to_str())?;
267			}
268			else {
269				state.serialize_field("type", &self.image.Type)?;
270			}
271			state.serialize_field("time_date_stamp", &self.image.TimeDateStamp)?;
272			state.serialize_field("version", &self.image.Version)?;
273			state.serialize_field("entry", &self.entry().ok())?;
274			state.end()
275		}
276	}
277}
278
279//----------------------------------------------------------------
280
281#[cfg(test)]
282pub(crate) fn test<'a, P: Pe<'a>>(pe: P) -> Result<()> {
283	let debug = pe.debug()?;
284	for dir in debug {
285		let _data = dir.data();
286		match dir.entry() {
287			Ok(Entry::CodeView(cv)) => {
288				let _format = cv.format();
289				let _pdb_file_name = cv.pdb_file_name();
290			},
291			Ok(Entry::Dbg(_dbg)) => (),
292			Ok(Entry::Pgo(pgo)) => {
293				for _sec in pgo {}
294			},
295			Ok(Entry::Unknown(_data)) => (),
296			Err(_) => (),
297		}
298	}
299	Ok(())
300}