diff --git a/examples/objdump.rs b/examples/objdump.rs index dc9076cd..aaa128a0 100644 --- a/examples/objdump.rs +++ b/examples/objdump.rs @@ -111,6 +111,16 @@ fn dump_object(data: &[u8]) { Ok(None) => {} Err(e) => println!("Failed to parse GNU debug alt link: {}", e), } + match file.pdb_info() { + Ok(Some(info)) => println!( + "PDB file: {}, GUID: {:x?}, Age: {}", + String::from_utf8_lossy(info.path()), + info.guid(), + info.age() + ), + Ok(None) => {} + Err(e) => println!("Failed to parse PE CodeView info: {}", e), + } for segment in file.segments() { println!("{:x?}", segment); diff --git a/src/read/any.rs b/src/read/any.rs index 6f0de4b7..d19649b8 100644 --- a/src/read/any.rs +++ b/src/read/any.rs @@ -13,8 +13,8 @@ use crate::read::pe; #[cfg(feature = "wasm")] use crate::read::wasm; use crate::read::{ - self, Architecture, BinaryFormat, ComdatKind, CompressedData, CompressedFileRange, Error, - Export, FileFlags, FileKind, Import, Object, ObjectComdat, ObjectMap, ObjectSection, + self, Architecture, BinaryFormat, CodeView, ComdatKind, CompressedData, CompressedFileRange, + Error, Export, FileFlags, FileKind, Import, Object, ObjectComdat, ObjectMap, ObjectSection, ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadRef, Relocation, Result, SectionFlags, SectionIndex, SectionKind, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapName, SymbolScope, SymbolSection, @@ -399,6 +399,11 @@ where with_inner!(self.inner, FileInternal, |x| x.gnu_debugaltlink()) } + #[inline] + fn pdb_info(&self) -> Result> { + with_inner!(self.inner, FileInternal, |x| x.pdb_info()) + } + fn entry(&self) -> u64 { with_inner!(self.inner, FileInternal, |x| x.entry()) } diff --git a/src/read/mod.rs b/src/read/mod.rs index 1dc5b627..8dbdf4c0 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -449,6 +449,34 @@ impl<'data> Export<'data> { } } +/// PDB Information +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CodeView<'data> { + guid: [u8; 16], + path: ByteString<'data>, + age: u32, +} + +impl<'data> CodeView<'data> { + /// The path to the PDB as stored in CodeView + #[inline] + pub fn path(&self) -> &'data [u8] { + self.path.0 + } + + /// The age of the PDB + #[inline] + pub fn age(&self) -> u32 { + self.age + } + + /// The GUID of the PDB. + #[inline] + pub fn guid(&self) -> [u8; 16] { + self.guid + } +} + /// The target referenced by a relocation. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum RelocationTarget { diff --git a/src/read/pe/file.rs b/src/read/pe/file.rs index 24f7099e..e79187af 100644 --- a/src/read/pe/file.rs +++ b/src/read/pe/file.rs @@ -2,12 +2,16 @@ use alloc::vec::Vec; use core::fmt::Debug; use core::{mem, str}; +use core::convert::TryInto; + use crate::read::coff::{CoffCommon, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, SymbolTable}; use crate::read::{ self, Architecture, ComdatKind, Error, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectComdat, ReadError, ReadRef, Result, SectionIndex, SymbolIndex, }; -use crate::{pe, ByteString, Bytes, LittleEndian as LE, Pod, U16Bytes, U32Bytes, U32, U64}; +use crate::{ + pe, ByteString, Bytes, CodeView, LittleEndian as LE, Pod, U16Bytes, U32Bytes, U32, U64, +}; use super::{PeSection, PeSectionIterator, PeSegment, PeSegmentIterator, SectionTable}; @@ -328,6 +332,57 @@ where Ok(exports) } + fn pdb_info(&self) -> Result> { + let data_dir = match self.data_directory(pe::IMAGE_DIRECTORY_ENTRY_DEBUG) { + Some(data_dir) => data_dir, + None => return Ok(None), + }; + let debug_data = data_dir.data(self.data, &self.common.sections).map(Bytes)?; + let debug_dir = debug_data + .read_at::(0) + .read_error("Invalid PE debug dir size")?; + + if debug_dir.typ.get(LE) != pe::IMAGE_DEBUG_TYPE_CODEVIEW { + return Ok(None); + } + + let info = self + .data + .read_slice_at::( + debug_dir.pointer_to_raw_data.get(LE) as u64, + debug_dir.size_of_data.get(LE) as usize, + ) + .read_error("Invalid CodeView Info address")?; + + let mut info = Bytes(info); + + let sig = info + .read_bytes(4) + .read_error("Invalid CodeView signature")?; + if sig.0 != b"RSDS" { + return Ok(None); + } + + let guid: [u8; 16] = info + .read_bytes(16) + .read_error("Invalid CodeView GUID")? + .0 + .try_into() + .unwrap(); + + let age = info.read::>().read_error("Invalid CodeView Age")?; + + let path = info + .read_string() + .read_error("Invalid CodeView file path")?; + + Ok(Some(CodeView { + path: ByteString(path), + guid, + age: age.get(LE), + })) + } + fn has_debug_symbols(&self) -> bool { self.section_by_name(".debug_info").is_some() } diff --git a/src/read/traits.rs b/src/read/traits.rs index 49a077d1..41f85d90 100644 --- a/src/read/traits.rs +++ b/src/read/traits.rs @@ -2,9 +2,9 @@ use alloc::borrow::Cow; use alloc::vec::Vec; use crate::read::{ - self, Architecture, ComdatKind, CompressedData, CompressedFileRange, Export, FileFlags, Import, - ObjectMap, Relocation, Result, SectionFlags, SectionIndex, SectionKind, SymbolFlags, - SymbolIndex, SymbolKind, SymbolMap, SymbolMapName, SymbolScope, SymbolSection, + self, Architecture, CodeView, ComdatKind, CompressedData, CompressedFileRange, Export, + FileFlags, Import, ObjectMap, Relocation, Result, SectionFlags, SectionIndex, SectionKind, + SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapName, SymbolScope, SymbolSection, }; use crate::Endianness; @@ -199,6 +199,12 @@ pub trait Object<'data: 'file, 'file>: read::private::Sealed { Ok(None) } + /// The filename and GUID from the PE CodeView section + #[inline] + fn pdb_info(&self) -> Result> { + Ok(None) + } + /// File flags that are specific to each file format. fn flags(&self) -> FileFlags; }