From 85948edf39f85d0bd8bfeec07875db3050c5125d Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Wed, 5 May 2021 07:43:20 -0700 Subject: [PATCH 1/6] Add ability to parse pdb info from PE file Just like we can get the UUID from macho files and the build-id from ELF files, this change allows users to pull out the PDB info (path, age, and GUID) from PE files. macho and ELF files will return `Ok(None)` --- examples/objdump.rs | 10 +++++++++ src/read/any.rs | 9 ++++++-- src/read/mod.rs | 30 ++++++++++++++++++++++++++- src/read/pe/file.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++- src/read/traits.rs | 12 ++++++++--- 5 files changed, 104 insertions(+), 7 deletions(-) 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..ccb5378c 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -5,7 +5,7 @@ use alloc::vec::Vec; use core::{fmt, result}; use crate::common::*; -use crate::{ByteString, Endianness}; +use crate::{ByteString, Bytes, Endianness}; mod read_ref; pub use read_ref::*; @@ -449,6 +449,34 @@ impl<'data> Export<'data> { } } +/// PDB Information +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CodeView<'data> { + guid: Bytes<'data>, + 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 PDB of the guid + #[inline] + pub fn guid(&self) -> &'data [u8] { + self.guid.0 + } +} + /// 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..7ea325e6 100644 --- a/src/read/pe/file.rs +++ b/src/read/pe/file.rs @@ -7,7 +7,9 @@ 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 +330,52 @@ 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 Err(Error("Invalid CodeView Signature")); + } + + let guid = info.read_bytes(16).read_error("Invalid CodeView GUID")?; + + 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; } From 0b3af6d99d3b37728c6a339a7e02eea056f850fc Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Wed, 5 May 2021 20:16:08 -0700 Subject: [PATCH 2/6] Fix comment "GUID of the PDB" Fix comment that does not make sense. Co-authored-by: Philip Craig --- src/read/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/read/mod.rs b/src/read/mod.rs index ccb5378c..7f1c2c6d 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -470,7 +470,7 @@ impl<'data> CodeView<'data> { self.age } - /// The PDB of the guid + /// The GUID of the PDB. #[inline] pub fn guid(&self) -> &'data [u8] { self.guid.0 From d3a686e6ee121835f6e73d813280e93d00a23cba Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Wed, 5 May 2021 20:18:00 -0700 Subject: [PATCH 3/6] Return None if signature doesn't match Co-authored-by: Philip Craig --- src/read/pe/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/read/pe/file.rs b/src/read/pe/file.rs index 7ea325e6..9a837b36 100644 --- a/src/read/pe/file.rs +++ b/src/read/pe/file.rs @@ -358,7 +358,7 @@ where .read_bytes(4) .read_error("Invalid CodeView signature")?; if sig.0 != b"RSDS" { - return Err(Error("Invalid CodeView Signature")); + return Ok(None); } let guid = info.read_bytes(16).read_error("Invalid CodeView GUID")?; From 1d3b7858071417ef6eb6de0dc853fa119402d622 Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Wed, 5 May 2021 20:31:57 -0700 Subject: [PATCH 4/6] Use `[u8; 16]` for PDB Guid type --- src/read/mod.rs | 8 ++++---- src/read/pe/file.rs | 9 ++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/read/mod.rs b/src/read/mod.rs index 7f1c2c6d..dd00d660 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -5,7 +5,7 @@ use alloc::vec::Vec; use core::{fmt, result}; use crate::common::*; -use crate::{ByteString, Bytes, Endianness}; +use crate::{ByteString, Endianness}; mod read_ref; pub use read_ref::*; @@ -452,7 +452,7 @@ impl<'data> Export<'data> { /// PDB Information #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct CodeView<'data> { - guid: Bytes<'data>, + guid: &'data [u8; 16], path: ByteString<'data>, age: u32, } @@ -472,8 +472,8 @@ impl<'data> CodeView<'data> { /// The GUID of the PDB. #[inline] - pub fn guid(&self) -> &'data [u8] { - self.guid.0 + pub fn guid(&self) -> &'data [u8; 16] { + self.guid } } diff --git a/src/read/pe/file.rs b/src/read/pe/file.rs index 9a837b36..2c3f0a22 100644 --- a/src/read/pe/file.rs +++ b/src/read/pe/file.rs @@ -2,6 +2,8 @@ use alloc::vec::Vec; use core::fmt::Debug; use core::{mem, str}; +use std::convert::TryInto; + use crate::read::coff::{CoffCommon, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, SymbolTable}; use crate::read::{ self, Architecture, ComdatKind, Error, Export, FileFlags, Import, NoDynamicRelocationIterator, @@ -361,7 +363,12 @@ where return Ok(None); } - let guid = info.read_bytes(16).read_error("Invalid CodeView GUID")?; + 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")?; From 3dfae0542d8b9b5ceae5b1f6f670f97be698627a Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Thu, 6 May 2021 06:38:36 -0700 Subject: [PATCH 5/6] Switch PDB Guid to be [u8;16] instead of reference --- src/read/mod.rs | 4 ++-- src/read/pe/file.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/read/mod.rs b/src/read/mod.rs index dd00d660..8dbdf4c0 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -452,7 +452,7 @@ impl<'data> Export<'data> { /// PDB Information #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct CodeView<'data> { - guid: &'data [u8; 16], + guid: [u8; 16], path: ByteString<'data>, age: u32, } @@ -472,7 +472,7 @@ impl<'data> CodeView<'data> { /// The GUID of the PDB. #[inline] - pub fn guid(&self) -> &'data [u8; 16] { + pub fn guid(&self) -> [u8; 16] { self.guid } } diff --git a/src/read/pe/file.rs b/src/read/pe/file.rs index 2c3f0a22..2d73589d 100644 --- a/src/read/pe/file.rs +++ b/src/read/pe/file.rs @@ -363,7 +363,7 @@ where return Ok(None); } - let guid: &[u8; 16] = info + let guid: [u8; 16] = info .read_bytes(16) .read_error("Invalid CodeView GUID")? .0 From c462a606b0452ff3d4ab8c70eaac4537d4a45a67 Mon Sep 17 00:00:00 2001 From: Matt Schulte Date: Thu, 6 May 2021 15:10:19 -0700 Subject: [PATCH 6/6] Use core::convert::TryInto Co-authored-by: Philip Craig --- src/read/pe/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/read/pe/file.rs b/src/read/pe/file.rs index 2d73589d..e79187af 100644 --- a/src/read/pe/file.rs +++ b/src/read/pe/file.rs @@ -2,7 +2,7 @@ use alloc::vec::Vec; use core::fmt::Debug; use core::{mem, str}; -use std::convert::TryInto; +use core::convert::TryInto; use crate::read::coff::{CoffCommon, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, SymbolTable}; use crate::read::{