Skip to content

Commit

Permalink
Synthesize sections from section map (fixes getsentry#153)
Browse files Browse the repository at this point in the history
  • Loading branch information
vvuk committed May 22, 2024
1 parent 7495395 commit edecc6f
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 2 deletions.
93 changes: 93 additions & 0 deletions src/dbi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,19 @@ impl<'s> DebugInformation<'s> {
let contributions_buf = buf.take(self.header.section_contribution_size as usize)?;
DBISectionContributionIter::parse(contributions_buf.into())
}

/// Returns an iterator that can traverse the section map in sequential order. Also known as the "OMF Segment map".
pub fn section_map(&self) -> Result<DBISectionMapIter<'_>> {
let mut buf = self.stream.parse_buffer();
// drop the header, modules list, and section contributions list
let offset = self.header_len
+ self.header.module_list_size as usize
+ self.header.section_contribution_size as usize;

buf.take(offset)?;
let section_map_buf = buf.take(self.header.section_map_size as usize)?;
DBISectionMapIter::parse(section_map_buf.into())
}
}

/// The version of the PDB format.
Expand Down Expand Up @@ -584,6 +597,86 @@ impl<'c> FallibleIterator for DBISectionContributionIter<'c> {
}
}

/// See https://github.com/google/syzygy/blob/8164b24ebde9c5649c9a09e88a7fc0b0fcbd1bc5/syzygy/pdb/pdb_data.h#L172
/// Also see https://www.virtualbox.org/browser/vbox/trunk/include/iprt/formats/codeview.h?rev=93115#L272
/// This is also known as OMF Segment Map. In the OMF SegmentMap structure, flags and section_type
/// are a single 16-bit value.
#[derive(Debug, Copy, Clone)]
pub struct DBISectionMapItem {
/// flags: 0x1 read, 0x2 write, 0x4 execute, 0x8 32-bit
pub flags: u8,
/// section_type: 0x1 = SEL, 0x2 = ABS, 0x10 = GROUP
pub section_type: u8,
/// Overlay number
pub overlay: u16,
/// group index, 0 if not relevant
pub group: u16,
/// Technically "frame" in OMF SegmentMap, which is complicated
pub section_number: u16,
/// Index into name table, or 0xffff
pub seg_name_index: u16,
/// Index into name table, or 0xffff
pub class_name_index: u16,
/// RVA offset of this section
pub rva_offset: u32,
/// Length of this section
pub section_length: u32,
}

impl DBISectionMapItem {
fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
Ok(Self {
flags: buf.parse_u8()?,
section_type: buf.parse_u8()?,
overlay: buf.parse_u16()?,
group: buf.parse_u16()?,
section_number: buf.parse_u16()?,
seg_name_index: buf.parse_u16()?,
class_name_index: buf.parse_u16()?,
rva_offset: buf.parse_u32()?,
section_length: buf.parse_u32()?,
})
}
}

/// A `DBISectionMapIter` iterates over the section map in the DBI section, producing `DBISectionMap`s.
#[derive(Debug)]
pub struct DBISectionMapIter<'c> {
/// The section count.
pub sec_count: u16,
/// The logical section count. Typically equals sec_count, if no groups are in use. (?)
pub sec_count_log: u16,
buf: ParseBuffer<'c>,
}

impl<'c> DBISectionMapIter<'c> {
fn parse(mut buf: ParseBuffer<'c>) -> Result<Self> {
let sec_count = buf.parse_u16()?;
let sec_count_log = buf.parse_u16()?;

Ok(Self {
buf,
sec_count,
sec_count_log,
})
}
}

impl<'c> FallibleIterator for DBISectionMapIter<'c> {
type Item = DBISectionMapItem;
type Error = Error;

fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> {
// see if we're at EOF
if self.buf.is_empty() {
return Ok(None);
}

let segmap = Self::Item::parse(&mut self.buf)?;
Ok(Some(segmap))
}
}

/// A `DbgDataHdr`, which contains a series of (optional) MSF stream numbers.
#[derive(Debug, Copy, Clone)]
#[allow(dead_code)] // reason = "unused fields added for completeness"
Expand Down
67 changes: 65 additions & 2 deletions src/pdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use crate::common::*;
use fallible_iterator::FallibleIterator;

use crate::dbi::{DBIExtraStreams, DBIHeader, DebugInformation, Module};
use crate::framedata::FrameTable;
use crate::modi::ModuleInfo;
Expand All @@ -17,6 +18,7 @@ use crate::source::Source;
use crate::strings::StringTable;
use crate::symbol::SymbolTable;
use crate::tpi::{IdInformation, TypeInformation};
use crate::{common::*, SectionCharacteristics};

// Some streams have a fixed stream index.
// http://llvm.org/docs/PDB/index.html
Expand Down Expand Up @@ -242,7 +244,7 @@ impl<'s, S: Source<'s> + 's> PDB<'s, S> {
let index = self.extra_streams()?.section_headers;
let stream = match self.raw_stream(index)? {
Some(stream) => stream,
None => return Ok(None),
None => return self.maybe_synthesize_section(),
};

let mut buf = stream.parse_buffer();
Expand All @@ -254,6 +256,67 @@ impl<'s, S: Source<'s> + 's> PDB<'s, S> {
Ok(Some(headers))
}

// If there are no section_headers in the file, attempt to synthesize sections
// based on the section map. This seems to be necessary to handle NGEN-generated PDB
// files (.ni.pdb from Crossgen2).
fn maybe_synthesize_section(&mut self) -> Result<Option<Vec<ImageSectionHeader>>> {
// If we have OMAP From data, I don't believe we can do this, because the RVAs
// won't map. But I'm not 100% sure of that, be conservative.
if self.omap_from_src()?.is_some() {
return Ok(None);
}

let debug_info = self.debug_information()?;
let sec_map = debug_info.section_map()?;
if sec_map.sec_count != sec_map.sec_count_log {
return Ok(None);
}
let sec_map = sec_map.collect::<Vec<_>>()?;

let mut rva = 0x1000u32; // in the absence of explicit section data, this starts at 0x1000
let sections = sec_map.into_iter()
.filter(|sm| {
// the section with a bogus section length also doesn't have any rwx flags,
// and has section_type == 2
sm.section_type == 1 && // "SEL" section, not ABS (0x2) or GROUP (0x10)
sm.section_length != u32::MAX // shouldn't happen, but just in case
})
.map(|sm| {
let mut characteristics = 0u32;
if sm.flags & 0x1 != 0 { // R
characteristics |= 0x40000000; // IMAGE_SCN_MEM_READ
}
if sm.flags & 0x2 != 0 { // W
characteristics |= 0x80000000; // IMAGE_SCN_MEM_WRITE
}
if sm.flags & 0x4 != 0 { // X
characteristics |= 0x20000000; // IMAGE_SCN_MEM_EXECUTE
characteristics |= 0x20; // IMAGE_SCN_CNT_CODE
}

if sm.rva_offset != 0 {
eprintln!("pdb: synthesizing section with rva_offset != 0, might not be correct! {:?}", sm);
}

let this_rva = rva + sm.rva_offset;
rva = this_rva + sm.section_length;
ImageSectionHeader {
name: [0; 8],
virtual_size: sm.section_length,
virtual_address: this_rva,
size_of_raw_data: sm.section_length,
pointer_to_raw_data: 0,
pointer_to_relocations: 0,
pointer_to_line_numbers: 0,
number_of_relocations: 0,
number_of_line_numbers: 0,
characteristics: SectionCharacteristics(characteristics),
}
}).collect::<Vec<_>>();

Ok(Some(sections))
}

/// Retrieve the global frame data table.
///
/// This table describes the stack frame layout for functions from all modules in the PDB. Not
Expand Down

0 comments on commit edecc6f

Please sign in to comment.