use crate::parser::{Parser, ParserError, ParserMut};
use crate::Container;
use crate::{get_or_init_err, Stream};
use crate::{StreamIndexIsNilError, StreamIndexU16};
use anyhow::{bail, Result};
use std::mem::size_of;
use std::ops::Range;
use sync_file::ReadAt;
use tracing::{error, warn};
use zerocopy::{
FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout, Unaligned, I32, LE, U16, U32,
};
#[cfg(doc)]
use crate::Pdb;
pub mod modules;
pub mod optional_dbg;
pub mod section_contrib;
pub mod section_map;
pub mod sources;
pub use modules::*;
#[doc(inline)]
pub use section_contrib::*;
#[doc(inline)]
pub use sources::*;
#[repr(C)]
#[derive(IntoBytes, FromBytes, KnownLayout, Immutable, Unaligned, Debug, Clone)]
#[allow(missing_docs)]
pub struct DbiStreamHeader {
pub signature: I32<LE>,
pub version: U32<LE>,
pub age: U32<LE>,
pub global_symbol_index_stream: StreamIndexU16,
pub build_number: U16<LE>,
pub public_symbol_index_stream: StreamIndexU16,
pub pdb_dll_version: U16<LE>,
pub global_symbol_stream: StreamIndexU16,
pub pdb_dll_rbld: U16<LE>,
pub mod_info_size: I32<LE>,
pub section_contribution_size: I32<LE>,
pub section_map_size: I32<LE>,
pub source_info_size: I32<LE>,
pub type_server_map_size: I32<LE>,
pub mfc_type_server_index: U32<LE>,
pub optional_dbg_header_size: I32<LE>,
pub edit_and_continue_size: I32<LE>,
pub flags: U16<LE>,
pub machine: U16<LE>,
pub padding: U32<LE>,
}
pub static EMPTY_DBI_STREAM_HEADER: [u8; DBI_STREAM_HEADER_LEN] = [
0xFF, 0xFF, 0xFF, 0xFF, 0x77, 0x09, 0x31, 0x01, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
#[test]
fn test_parse_empty_dbi_stream_header() {
let h = DbiStreamHeader::read_from_bytes(EMPTY_DBI_STREAM_HEADER.as_slice()).unwrap();
assert!(h.global_symbol_index_stream.get().is_none());
}
impl DbiStreamHeader {
pub fn sym_record_stream(&self) -> Result<u32, StreamIndexIsNilError> {
self.global_symbol_stream.get_err()
}
pub fn public_stream_index(&self) -> Result<u32, StreamIndexIsNilError> {
self.public_symbol_index_stream.get_err()
}
pub fn global_stream_index(&self) -> Result<u32, StreamIndexIsNilError> {
self.global_symbol_index_stream.get_err()
}
pub fn modules_range(&self) -> anyhow::Result<Range<usize>> {
let start = DBI_STREAM_HEADER_LEN;
let size = self.mod_info_size.get() as usize;
Ok(start..start + size)
}
pub fn sources_range(&self) -> anyhow::Result<Range<usize>> {
let start = DBI_STREAM_HEADER_LEN
+ self.mod_info_size.get() as usize
+ self.section_contribution_size.get() as usize
+ self.section_map_size.get() as usize;
let size = self.source_info_size.get() as usize;
Ok(start..start + size)
}
}
static_assertions::const_assert_eq!(size_of::<DbiStreamHeader>(), DBI_STREAM_HEADER_LEN);
const DBI_STREAM_HEADER_LEN: usize = 64;
pub const DBI_STREAM_VERSION_VC41: u32 = 930803;
pub const DBI_STREAM_VERSION_V50: u32 = 19960307;
pub const DBI_STREAM_VERSION_V60: u32 = 19970606;
pub const DBI_STREAM_VERSION_V70: u32 = 19990903;
pub const DBI_STREAM_VERSION_V110: u32 = 20091201;
#[derive(Clone)]
pub struct DbiStream<StreamData = Vec<u8>>
where
StreamData: AsRef<[u8]>,
{
pub stream_data: StreamData,
pub substreams: DbiSubstreamRanges,
}
macro_rules! dbi_substreams {
(
$(
$name:ident,
$mut_name:ident,
$size_field:ident ;
)*
) => {
#[derive(Clone, Debug, Default)]
pub struct DbiSubstreamRanges {
$(
#[doc = concat!("The range of the ", stringify!($name), " substream.")]
pub $name: Range<usize>,
)*
}
impl<StreamData: AsRef<[u8]>> DbiStream<StreamData> {
$(
#[doc = concat!("The unparsed contents of the ", stringify!($name), " substream.")]
pub fn $name(&self) -> &[u8] {
self.substream_data(self.substreams.$name.clone())
}
#[doc = concat!("The unparsed contents of the ", stringify!($name), " substream.")]
pub fn $mut_name(&mut self) -> &mut [u8]
where
StreamData: AsMut<[u8]>,
{
self.substream_data_mut(self.substreams.$name.clone())
}
)*
}
impl DbiSubstreamRanges {
pub(crate) fn from_sizes(sizes: &DbiStreamHeader, stream_len: usize) -> anyhow::Result<Self> {
let mut pos: usize = DBI_STREAM_HEADER_LEN;
if pos > stream_len {
bail!("DBI stream is too short; pos = {}, stream_len = {}", pos, stream_len);
}
$(
assert!(pos <= stream_len);
let size: i32 = sizes.$size_field.get();
if size < 0 {
bail!("Substream {} length in DBI header is invalid (is negative)", stringify!($size_field));
}
let len = size as usize;
let available = stream_len - pos;
if len > available {
bail!("Substream {} length in DBI header is invalid. It extends beyond the end of the stream.", stringify!($size_field));
}
let start = pos;
pos += len;
let $name = start..pos;
)*
if pos < stream_len {
warn!(pos, stream_len, "Something is wrong with the code that finds the ranges of substreams. Expected pos to be equal to stream_len.");
} else if pos > stream_len {
error!(pos, stream_len, "Something is very wrong with the DBI header. The sum of the subtream lengths (pos) exceeds the stream len.");
} else {
}
Ok(Self {
$( $name, )*
})
}
}
}
}
dbi_substreams! {
modules_bytes, modules_bytes_mut, mod_info_size;
section_contributions_bytes, section_contributions_bytes_mut, section_contribution_size;
section_map_bytes, section_map_bytes_mut, section_map_size;
source_info, source_info_mut, source_info_size;
type_server_map, type_server_map_mut, type_server_map_size;
edit_and_continue, edit_and_continue_mut, edit_and_continue_size;
optional_debug_header_bytes, optional_debug_header_bytes_mut, optional_dbg_header_size;
}
impl<StreamData: AsRef<[u8]>> DbiStream<StreamData> {
pub fn header(&self) -> Result<&DbiStreamHeader> {
if let Ok((header, _)) = DbiStreamHeader::ref_from_prefix(self.stream_data.as_ref()) {
Ok(header)
} else {
bail!("The DBI stream is too small to contain a valid header.")
}
}
pub fn header_mut(&mut self) -> Result<&mut DbiStreamHeader>
where
StreamData: AsMut<[u8]>,
{
if let Ok((header, _)) = DbiStreamHeader::mut_from_prefix(self.stream_data.as_mut()) {
Ok(header)
} else {
bail!("The DBI stream is too small to contain a valid header.")
}
}
fn substream_data(&self, range: Range<usize>) -> &[u8] {
&self.stream_data.as_ref()[range]
}
fn substream_data_mut(&mut self, range: Range<usize>) -> &mut [u8]
where
StreamData: AsMut<[u8]>,
{
&mut self.stream_data.as_mut()[range]
}
pub fn modules(&self) -> ModInfoSubstream<&[u8]> {
ModInfoSubstream {
substream_data: self.modules_bytes(),
}
}
pub fn iter_modules(&self) -> IterModuleInfo<'_> {
IterModuleInfo::new(self.modules_bytes())
}
pub fn iter_modules_mut(&mut self) -> IterModuleInfoMut<'_>
where
StreamData: AsMut<[u8]>,
{
IterModuleInfoMut::new(self.modules_bytes_mut())
}
pub fn as_slice(&self) -> DbiStream<&[u8]> {
DbiStream {
stream_data: self.stream_data.as_ref(),
substreams: self.substreams.clone(),
}
}
pub fn parse(stream_data: StreamData) -> anyhow::Result<Self> {
let stream_bytes: &[u8] = stream_data.as_ref();
if stream_bytes.is_empty() {
return Ok(Self {
substreams: Default::default(),
stream_data,
});
}
let mut p = Parser::new(stream_bytes);
let dbi_header: &DbiStreamHeader = p.get()?;
let substreams = DbiSubstreamRanges::from_sizes(dbi_header, stream_bytes.len())?;
Ok(Self {
stream_data,
substreams,
})
}
pub fn sources(&self) -> anyhow::Result<sources::DbiSourcesSubstream<'_>> {
DbiSourcesSubstream::parse(self.source_info())
}
pub fn section_contributions(
&self,
) -> anyhow::Result<section_contrib::SectionContributionsSubstream<'_>> {
let substream_bytes = self.section_contributions_bytes();
section_contrib::SectionContributionsSubstream::parse(substream_bytes)
}
pub fn section_map(&self) -> anyhow::Result<section_map::SectionMap<'_>> {
let section_map_bytes = self.section_map_bytes();
section_map::SectionMap::parse(section_map_bytes)
}
pub fn optional_debug_header(&self) -> anyhow::Result<optional_dbg::OptionalDebugHeader> {
optional_dbg::OptionalDebugHeader::parse(self.optional_debug_header_bytes())
}
pub fn optional_debug_header_mut(&mut self) -> anyhow::Result<&mut [U16<LE>]>
where
StreamData: AsMut<[u8]>,
{
if self.substreams.optional_debug_header_bytes.is_empty() {
Ok(&mut [])
} else {
let substream_bytes =
&mut self.stream_data.as_mut()[self.substreams.optional_debug_header_bytes.clone()];
if let Ok(slice) = <[U16<LE>]>::mut_from_bytes(substream_bytes) {
Ok(slice)
} else {
bail!("The Optional Debug Header substream within the DBI stream is malformed (length is not valid).");
}
}
}
}
pub fn read_dbi_stream_header<F: ReadAt>(msf: &Container<F>) -> anyhow::Result<DbiStreamHeader> {
let stream_reader = msf.get_stream_reader(Stream::DBI.into())?;
if !stream_reader.is_empty() {
let mut dbi_header = DbiStreamHeader::new_zeroed();
stream_reader.read_exact_at(dbi_header.as_mut_bytes(), 0)?;
Ok(dbi_header)
} else {
Ok(DbiStreamHeader::read_from_bytes(EMPTY_DBI_STREAM_HEADER.as_slice()).unwrap())
}
}
pub fn read_dbi_stream<F: ReadAt>(
container: &Container<F>,
) -> Result<DbiStream<Vec<u8>>, anyhow::Error> {
let mut dbi_stream_data = container.read_stream_to_vec(Stream::DBI.into())?;
if dbi_stream_data.is_empty() {
dbi_stream_data = EMPTY_DBI_STREAM_HEADER.to_vec();
}
DbiStream::parse(dbi_stream_data)
}
impl<F: ReadAt> crate::Pdb<F> {
pub fn read_dbi_stream_header(&self) -> anyhow::Result<DbiStreamHeader> {
read_dbi_stream_header(&self.container)
}
pub fn read_dbi_stream(&self) -> Result<DbiStream<Vec<u8>>, anyhow::Error> {
read_dbi_stream(&self.container)
}
fn read_dbi_substream(&self, range: Range<usize>) -> anyhow::Result<Vec<u8>> {
let len = range.len();
let mut substream_data = vec![0; len];
let reader = self.container.get_stream_reader(Stream::DBI.into())?;
reader.read_exact_at(&mut substream_data, range.start as u64)?;
Ok(substream_data)
}
pub fn read_modules(&self) -> anyhow::Result<ModInfoSubstream<Vec<u8>>> {
let substream_data = self.read_dbi_substream(self.dbi_substreams.modules_bytes.clone())?;
Ok(ModInfoSubstream { substream_data })
}
pub fn modules(&self) -> anyhow::Result<&ModInfoSubstream<Vec<u8>>> {
get_or_init_err(&self.dbi_modules_cell, || self.read_modules())
}
pub fn read_sources_data(&self) -> Result<Vec<u8>> {
self.read_dbi_substream(self.dbi_substreams.source_info.clone())
}
pub fn sources_data(&self) -> Result<&[u8]> {
let sources_data = get_or_init_err(&self.dbi_sources_cell, || self.read_sources_data())?;
Ok(sources_data)
}
pub fn sources(&self) -> Result<sources::DbiSourcesSubstream<'_>> {
let sources_data = self.sources_data()?;
sources::DbiSourcesSubstream::parse(sources_data)
}
pub fn drop_sources(&mut self) {
self.dbi_sources_cell = Default::default();
}
pub fn read_section_contributions(&self) -> Result<Vec<u8>> {
self.read_dbi_substream(self.dbi_substreams.section_contributions_bytes.clone())
}
}
pub fn validate_dbi_stream(stream_data: &[u8]) -> anyhow::Result<()> {
let dbi_stream = DbiStream::parse(stream_data)?;
let num_modules: usize = dbi_stream.modules().iter().count();
let sources = DbiSourcesSubstream::parse(dbi_stream.source_info())?;
if sources.num_modules() != num_modules {
bail!("Number of modules found in Sources substream ({}) does not match number of Module Info structs found in Modules substream ({}).",
sources.num_modules(),
num_modules
);
}
Ok(())
}