Skip to content

Commit 2e0af3f

Browse files
authored
Add ability to parse pdb info from PE file (#298)
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)`
1 parent 45cc193 commit 2e0af3f

File tree

5 files changed

+110
-6
lines changed

5 files changed

+110
-6
lines changed

examples/objdump.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ fn dump_object(data: &[u8]) {
111111
Ok(None) => {}
112112
Err(e) => println!("Failed to parse GNU debug alt link: {}", e),
113113
}
114+
match file.pdb_info() {
115+
Ok(Some(info)) => println!(
116+
"PDB file: {}, GUID: {:x?}, Age: {}",
117+
String::from_utf8_lossy(info.path()),
118+
info.guid(),
119+
info.age()
120+
),
121+
Ok(None) => {}
122+
Err(e) => println!("Failed to parse PE CodeView info: {}", e),
123+
}
114124

115125
for segment in file.segments() {
116126
println!("{:x?}", segment);

src/read/any.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use crate::read::pe;
1313
#[cfg(feature = "wasm")]
1414
use crate::read::wasm;
1515
use crate::read::{
16-
self, Architecture, BinaryFormat, ComdatKind, CompressedData, CompressedFileRange, Error,
17-
Export, FileFlags, FileKind, Import, Object, ObjectComdat, ObjectMap, ObjectSection,
16+
self, Architecture, BinaryFormat, CodeView, ComdatKind, CompressedData, CompressedFileRange,
17+
Error, Export, FileFlags, FileKind, Import, Object, ObjectComdat, ObjectMap, ObjectSection,
1818
ObjectSegment, ObjectSymbol, ObjectSymbolTable, ReadRef, Relocation, Result, SectionFlags,
1919
SectionIndex, SectionKind, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapName,
2020
SymbolScope, SymbolSection,
@@ -399,6 +399,11 @@ where
399399
with_inner!(self.inner, FileInternal, |x| x.gnu_debugaltlink())
400400
}
401401

402+
#[inline]
403+
fn pdb_info(&self) -> Result<Option<CodeView>> {
404+
with_inner!(self.inner, FileInternal, |x| x.pdb_info())
405+
}
406+
402407
fn entry(&self) -> u64 {
403408
with_inner!(self.inner, FileInternal, |x| x.entry())
404409
}

src/read/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,34 @@ impl<'data> Export<'data> {
449449
}
450450
}
451451

452+
/// PDB Information
453+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
454+
pub struct CodeView<'data> {
455+
guid: [u8; 16],
456+
path: ByteString<'data>,
457+
age: u32,
458+
}
459+
460+
impl<'data> CodeView<'data> {
461+
/// The path to the PDB as stored in CodeView
462+
#[inline]
463+
pub fn path(&self) -> &'data [u8] {
464+
self.path.0
465+
}
466+
467+
/// The age of the PDB
468+
#[inline]
469+
pub fn age(&self) -> u32 {
470+
self.age
471+
}
472+
473+
/// The GUID of the PDB.
474+
#[inline]
475+
pub fn guid(&self) -> [u8; 16] {
476+
self.guid
477+
}
478+
}
479+
452480
/// The target referenced by a relocation.
453481
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
454482
pub enum RelocationTarget {

src/read/pe/file.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ use alloc::vec::Vec;
22
use core::fmt::Debug;
33
use core::{mem, str};
44

5+
use core::convert::TryInto;
6+
57
use crate::read::coff::{CoffCommon, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, SymbolTable};
68
use crate::read::{
79
self, Architecture, ComdatKind, Error, Export, FileFlags, Import, NoDynamicRelocationIterator,
810
Object, ObjectComdat, ReadError, ReadRef, Result, SectionIndex, SymbolIndex,
911
};
10-
use crate::{pe, ByteString, Bytes, LittleEndian as LE, Pod, U16Bytes, U32Bytes, U32, U64};
12+
use crate::{
13+
pe, ByteString, Bytes, CodeView, LittleEndian as LE, Pod, U16Bytes, U32Bytes, U32, U64,
14+
};
1115

1216
use super::{PeSection, PeSectionIterator, PeSegment, PeSegmentIterator, SectionTable};
1317

@@ -328,6 +332,57 @@ where
328332
Ok(exports)
329333
}
330334

335+
fn pdb_info(&self) -> Result<Option<CodeView>> {
336+
let data_dir = match self.data_directory(pe::IMAGE_DIRECTORY_ENTRY_DEBUG) {
337+
Some(data_dir) => data_dir,
338+
None => return Ok(None),
339+
};
340+
let debug_data = data_dir.data(self.data, &self.common.sections).map(Bytes)?;
341+
let debug_dir = debug_data
342+
.read_at::<pe::ImageDebugDirectory>(0)
343+
.read_error("Invalid PE debug dir size")?;
344+
345+
if debug_dir.typ.get(LE) != pe::IMAGE_DEBUG_TYPE_CODEVIEW {
346+
return Ok(None);
347+
}
348+
349+
let info = self
350+
.data
351+
.read_slice_at::<u8>(
352+
debug_dir.pointer_to_raw_data.get(LE) as u64,
353+
debug_dir.size_of_data.get(LE) as usize,
354+
)
355+
.read_error("Invalid CodeView Info address")?;
356+
357+
let mut info = Bytes(info);
358+
359+
let sig = info
360+
.read_bytes(4)
361+
.read_error("Invalid CodeView signature")?;
362+
if sig.0 != b"RSDS" {
363+
return Ok(None);
364+
}
365+
366+
let guid: [u8; 16] = info
367+
.read_bytes(16)
368+
.read_error("Invalid CodeView GUID")?
369+
.0
370+
.try_into()
371+
.unwrap();
372+
373+
let age = info.read::<U32<LE>>().read_error("Invalid CodeView Age")?;
374+
375+
let path = info
376+
.read_string()
377+
.read_error("Invalid CodeView file path")?;
378+
379+
Ok(Some(CodeView {
380+
path: ByteString(path),
381+
guid,
382+
age: age.get(LE),
383+
}))
384+
}
385+
331386
fn has_debug_symbols(&self) -> bool {
332387
self.section_by_name(".debug_info").is_some()
333388
}

src/read/traits.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use alloc::borrow::Cow;
22
use alloc::vec::Vec;
33

44
use crate::read::{
5-
self, Architecture, ComdatKind, CompressedData, CompressedFileRange, Export, FileFlags, Import,
6-
ObjectMap, Relocation, Result, SectionFlags, SectionIndex, SectionKind, SymbolFlags,
7-
SymbolIndex, SymbolKind, SymbolMap, SymbolMapName, SymbolScope, SymbolSection,
5+
self, Architecture, CodeView, ComdatKind, CompressedData, CompressedFileRange, Export,
6+
FileFlags, Import, ObjectMap, Relocation, Result, SectionFlags, SectionIndex, SectionKind,
7+
SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapName, SymbolScope, SymbolSection,
88
};
99
use crate::Endianness;
1010

@@ -199,6 +199,12 @@ pub trait Object<'data: 'file, 'file>: read::private::Sealed {
199199
Ok(None)
200200
}
201201

202+
/// The filename and GUID from the PE CodeView section
203+
#[inline]
204+
fn pdb_info(&self) -> Result<Option<CodeView>> {
205+
Ok(None)
206+
}
207+
202208
/// File flags that are specific to each file format.
203209
fn flags(&self) -> FileFlags;
204210
}

0 commit comments

Comments
 (0)