Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions examples/pdb2hpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use pdb::FallibleIterator;

type TypeSet = BTreeSet<pdb::TypeIndex>;

pub fn type_name<'p>(
type_finder: &pdb::TypeFinder<'p>,
pub fn type_name(
type_finder: &pdb::TypeFinder<'_>,
type_index: pdb::TypeIndex,
needed_types: &mut TypeSet,
) -> pdb::Result<String> {
Expand Down Expand Up @@ -359,8 +359,8 @@ impl<'p> Method<'p> {
}
}

fn argument_list<'p>(
type_finder: &pdb::TypeFinder<'p>,
fn argument_list(
type_finder: &pdb::TypeFinder<'_>,
type_index: pdb::TypeIndex,
needed_types: &mut TypeSet,
) -> pdb::Result<Vec<String>> {
Expand Down
11 changes: 10 additions & 1 deletion examples/pdb_symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::env;
use pdb2 as pdb;

use getopts::Options;
use pdb::{FallibleIterator, PdbInternalSectionOffset};
use pdb::{FallibleIterator, PdbInternalSectionOffset, RawString};

fn print_usage(program: &str, opts: Options) {
let brief = format!("Usage: {} input.pdb", program);
Expand All @@ -28,6 +28,15 @@ fn print_symbol(symbol: &pdb::Symbol<'_>) -> pdb::Result<()> {
pdb::SymbolData::Procedure(data) => {
print_row(data.offset, "function", data.name);
}
pdb::SymbolData::ManagedProcedure(data) => {
match data.name {
None => print_row(data.offset, "function", RawString::from(&b"<empty>"[..])),
Some(name) => print_row(data.offset, "function", name),
}
}
pdb::SymbolData::ManagedSlot(data) => {
print_row(data.offset, "data", data.name);
}
_ => {
// ignore everything else
}
Expand Down
12 changes: 10 additions & 2 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::result;
use std::slice;

use scroll::ctx::TryFromCtx;
use scroll::{self, Endian, Pread, LE};
use scroll::{Endian, Pread, LE};

use crate::tpi::constants;

Expand Down Expand Up @@ -581,7 +581,7 @@ pub trait ItemIndex:
/// [`ModuleInfo`](crate::ModuleInfo). Note that this comparison needs to be done
/// case-insensitively as the name in the DBI stream and name table are known to not
/// have matching cases.
/// 4. Resolve the [`Local`](crate::Local) index into a global one using
/// 4. Resolve the [`Local`] index into a global one using
/// [`CrossModuleExports`](crate::CrossModuleExports).
///
/// Cross module references are specially formatted indexes with the most significant bit set to
Expand All @@ -605,6 +605,14 @@ impl_pread!(TypeIndex);

impl ItemIndex for TypeIndex {}

/// COM+ metadata token for managed procedures (`CV_tkn_t`).
#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct COMToken(pub u32);

impl_convert!(COMToken, u32);
impl_hex_fmt!(COMToken);
impl_pread!(COMToken);

/// Index of an [`Id`](crate::Id) in [`IdInformation`](crate::IdInformation) stream.
///
/// If this index is a [cross module reference](ItemIndex::is_cross_module), it must be resolved
Expand Down
11 changes: 11 additions & 0 deletions src/dbi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@ impl<'s> DebugInformation<'s> {
}
}

/// Returns whether or not this PDB has been marked as stripped. Stripped PDBs do not contain
/// type information, line number information, or per-object CV symbols.
///
/// This flag is set when a PDB is written with [/PDBSTRIPPED] by MSVC.
///
/// [/PDBSTRIPPED]: https://learn.microsoft.com/en-us/cpp/build/reference/pdbstripped-strip-private-symbols?view=msvc-170
pub fn is_stripped(&self) -> bool {
// flags.fStripped
(self.header.flags & 0x2) != 0
}

/// Returns an iterator that can traverse the modules list in sequential order.
pub fn modules(&self) -> Result<ModuleIter<'_>> {
let mut buf = self.stream.parse_buffer();
Expand Down
7 changes: 3 additions & 4 deletions src/modi/c13.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use scroll::{ctx::TryFromCtx, Endian, Pread};

use crate::common::*;
use crate::modi::{
constants, CrossModuleExport, CrossModuleRef, FileChecksum, FileIndex, FileInfo, LineInfo,
LineInfoKind, ModuleRef,
constants, CrossModuleExport, CrossModuleRef, FileChecksum, FileInfo, LineInfo, LineInfoKind,
ModuleRef,
};
use crate::symbol::{BinaryAnnotation, BinaryAnnotationsIter, InlineSiteSymbol};
use crate::FallibleIterator;
Expand Down Expand Up @@ -345,6 +345,7 @@ enum LineEntry {
/// Declares a source line number.
Number(LineNumberEntry),
/// Declares a debugging marker.
#[allow(dead_code)] // reason = "the inner `LineMarkerEntry` is not (yet) accessed"
Marker(LineMarkerEntry),
}

Expand Down Expand Up @@ -1418,8 +1419,6 @@ impl<'a> LineProgram<'a> {
mod tests {
use super::*;

use std::mem;

use crate::symbol::BinaryAnnotations;

#[test]
Expand Down
2 changes: 1 addition & 1 deletion src/modi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl<'s> ModuleInfo<'s> {
}

fn lines_data(&self, size: usize) -> &[u8] {
let start = self.symbols_size as usize;
let start = self.symbols_size;
&self.stream[start..start + size]
}

Expand Down
204 changes: 199 additions & 5 deletions src/msf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ mod big {
let _ = stream_table.take((stream_count - stream_number - 1) as usize * 4)?;

// skip the preceding streams' page numbers
let _ = stream_table.take((page_numbers_to_skip as usize) * 4)?;
let _ = stream_table.take(page_numbers_to_skip * 4)?;

// we're now at the list of pages for our stream
// accumulate them into a PageList
Expand Down Expand Up @@ -344,8 +344,201 @@ mod big {
}

mod small {
pub const MAGIC: &[u8] = b"Microsoft C/C++ program database 2.00\r\n\x1a\x4a\x47";
// TODO: implement SmallMSF
use super::*;

pub const MAGIC: &[u8; 44] = b"Microsoft C/C++ program database 2.00\r\n\x1a\x4a\x47\x00\x00";

/// The PDB header as stored on disk.
///
/// See the Microsoft code for reference: <https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/msf/msf.cpp#L933>
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct RawHeader {
magic: [u8; 44],
page_size: u32,
free_page_map: u16,
pages_used: u16,
directory_size: u32,
_reserved: u32,
}

impl<'t> TryFromCtx<'t, Endian> for RawHeader {
type Error = scroll::Error;

fn try_from_ctx(
this: &'t [u8],
le: Endian,
) -> std::result::Result<(Self, usize), Self::Error> {
let mut offset = 0;
let data = Self {
magic: {
let mut tmp = [0; 44];
this.gread_inout_with(&mut offset, &mut tmp, le)?;
tmp
},
page_size: this.gread_with(&mut offset, le)?,
free_page_map: this.gread_with(&mut offset, le)?,
pages_used: this.gread_with(&mut offset, le)?,
directory_size: this.gread_with(&mut offset, le)?,
_reserved: this.gread_with(&mut offset, le)?,
};
Ok((data, offset))
}
}

#[derive(Debug)]
pub struct SmallMSF<'s, S> {
header: Header,
source: S,
stream_table: StreamTable<'s>,
}

impl<'s, S: Source<'s>> SmallMSF<'s, S> {
pub fn new(mut source: S, header_view: Box<dyn SourceView<'_>>) -> Result<SmallMSF<'s, S>> {
let mut buf = ParseBuffer::from(header_view.as_slice());

let header: RawHeader = buf.parse()?;

if &header.magic != MAGIC {
return Err(Error::UnrecognizedFileFormat);
}

// TODO: check if this is correct for small MSF
if header.page_size.count_ones() != 1
|| header.page_size < 0x100
|| header.page_size > (128 * 0x10000)
{
return Err(Error::InvalidPageSize(header.page_size as _));
}

let header_object = Header {
page_size: header.page_size as _,
maximum_valid_page_number: header.pages_used as _,
};

// build the stream table page list
let mut stream_table_page_list = PageList::new(header_object.page_size);
let mut i = 0;

while i < header.directory_size {
let n = buf.parse_u16()? as u32;
stream_table_page_list.push(header_object.validate_page_number(n)?);
i += header.page_size;
}

// truncate the stream table page list to the correct size
stream_table_page_list.truncate(header.directory_size as _);

let stream_table_view = view(&mut source, &stream_table_page_list)?;

Ok(SmallMSF {
header: header_object,
source,
stream_table: StreamTable::Available { stream_table_view },
})
}

fn look_up_stream(&mut self, stream_number: u32) -> Result<PageList> {
// ensure the stream table is available
let StreamTable::Available {
ref stream_table_view,
} = self.stream_table else {
unreachable!()
};

let stream_table_slice = stream_table_view.as_slice();
let mut stream_table = ParseBuffer::from(stream_table_slice);

// the stream table is structured as:
// stream_count: u16
// reserved: u16
// for _ in 0..stream_count:
// size of stream in bytes: u32 (0xffffffff indicating "stream does not exist")
// reserved: u32
// stream 0: PageNumber: u16
// stream 1: PageNumber: u16, PageNumber: u16
// stream 2: PageNumber: u16, PageNumber: u16, PageNumber; u16, PageNumber: u16, PageNumber: u16
// stream 3: PageNumber: u16, PageNumber: u16, PageNumber; u16, PageNumber: u16
// (number of pages determined by number of bytes)

let stream_count = stream_table.parse_u16()? as u32;
let _reserved = stream_table.parse_u16()?;

// check if we've already outworn our welcome
if stream_number >= stream_count {
return Err(Error::StreamNotFound(stream_number));
}

// we now have {stream_count} u32s describing the length of each stream

// walk over the streams before the requested stream
// we need to pay attention to how big each one is, since their page numbers come
// before our page numbers in the stream table
let mut page_numbers_to_skip: usize = 0;

for _ in 0..stream_number {
let bytes = stream_table.parse_u32()?;
let _reserved = stream_table.parse_u32()?;

if bytes == u32::max_value() {
// stream is not present, ergo nothing to skip
} else {
page_numbers_to_skip += self.header.pages_needed_to_store(bytes as usize);
}
}

// read our stream's size
let bytes_in_stream = stream_table.parse_u32()?;
let _reserved = stream_table.parse_u32()?;

if bytes_in_stream == u32::max_value() {
return Err(Error::StreamNotFound(stream_number));
}

let pages_in_stream = self.header.pages_needed_to_store(bytes_in_stream as usize);

// skip the remaining streams' byte counts
let _ = stream_table.take((stream_count - stream_number - 1) as usize * 8)?;

// skip the preceding streams' page numbers
let _ = stream_table.take(page_numbers_to_skip * 2)?;

// we're now at the list of pages for our stream
// accumulate them into a PageList
let mut page_list = PageList::new(self.header.page_size);

for _ in 0..pages_in_stream {
let page_number = stream_table.parse_u16()? as u32;
page_list.push(self.header.validate_page_number(page_number)?);
}

// truncate to the size of the stream
page_list.truncate(bytes_in_stream as usize);

// done!
Ok(page_list)
}
}

impl<'s, S: Source<'s>> Msf<'s, S> for SmallMSF<'s, S> {
fn get(&mut self, stream_number: u32, limit: Option<usize>) -> Result<Stream<'s>> {
// look up the stream
let mut page_list = self.look_up_stream(stream_number)?;

// apply any limits we have
if let Some(limit) = limit {
page_list.truncate(limit);
}

// now that we know where this stream lives, we can view it
let view = view(&mut self.source, &page_list)?;

// pack it into a Stream
let stream = Stream { source_view: view };

Ok(stream)
}
}
}

/// Represents a single Stream within the multi-stream file.
Expand Down Expand Up @@ -414,8 +607,9 @@ pub fn open_msf<'s, S: Source<'s> + Send + 's>(
}

if header_matches(header_view.as_slice(), small::MAGIC) {
// sorry
return Err(Error::UnimplementedFeature("small MSF file format"));
// claimed!
let smallmsf = small::SmallMSF::new(source, header_view)?;
return Ok(Box::new(smallmsf));
}

Err(Error::UnrecognizedFileFormat)
Expand Down
1 change: 0 additions & 1 deletion src/msf/page_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ impl PageList {
#[cfg(test)]
mod tests {
use crate::msf::page_list::*;
use crate::source::SourceSlice;

#[test]
fn test_push() {
Expand Down
4 changes: 1 addition & 3 deletions src/omap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl fmt::Debug for OMAPRecord {
impl PartialOrd for OMAPRecord {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.source_address().partial_cmp(&other.source_address())
Some(self.cmp(other))
}
}

Expand Down Expand Up @@ -577,8 +577,6 @@ impl PdbInternalSectionOffset {
mod tests {
use super::*;

use std::mem;

#[test]
fn test_omap_record() {
assert_eq!(mem::size_of::<OMAPRecord>(), 8);
Expand Down
Loading