Skip to content

Commit a216f25

Browse files
committed
Synthesize sections from section map (fixes #153)
1 parent 7495395 commit a216f25

File tree

2 files changed

+156
-2
lines changed

2 files changed

+156
-2
lines changed

src/dbi.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,19 @@ impl<'s> DebugInformation<'s> {
107107
let contributions_buf = buf.take(self.header.section_contribution_size as usize)?;
108108
DBISectionContributionIter::parse(contributions_buf.into())
109109
}
110+
111+
/// Returns an iterator that can traverse the section map in sequential order. Also known as the "OMF Segment map".
112+
pub fn section_map(&self) -> Result<DBISectionMapIter<'_>> {
113+
let mut buf = self.stream.parse_buffer();
114+
// drop the header, modules list, and section contributions list
115+
let offset = self.header_len +
116+
self.header.module_list_size as usize +
117+
self.header.section_contribution_size as usize;
118+
119+
buf.take(offset)?;
120+
let section_map_buf = buf.take(self.header.section_map_size as usize)?;
121+
DBISectionMapIter::parse(section_map_buf.into())
122+
}
110123
}
111124

112125
/// The version of the PDB format.
@@ -584,6 +597,83 @@ impl<'c> FallibleIterator for DBISectionContributionIter<'c> {
584597
}
585598
}
586599

600+
/// See https://github.com/google/syzygy/blob/8164b24ebde9c5649c9a09e88a7fc0b0fcbd1bc5/syzygy/pdb/pdb_data.h#L172
601+
/// Also see https://www.virtualbox.org/browser/vbox/trunk/include/iprt/formats/codeview.h?rev=93115#L272
602+
/// This is also known as OMF Segment Map. In the OMF SegmentMap structure, flags and section_type
603+
/// are a single 16-bit value.
604+
#[derive(Debug, Copy, Clone)]
605+
pub struct DBISectionMapItem {
606+
/// flags: 0x1 read, 0x2 write, 0x4 execute, 0x8 32-bit
607+
pub flags: u8,
608+
/// section_type: 0x1 = SEL, 0x2 = ABS, 0x10 = GROUP
609+
pub section_type: u8,
610+
/// Overlay number
611+
pub overlay: u16,
612+
/// group index, 0 if not relevant
613+
pub group: u16,
614+
/// Technically "frame" in OMF SegmentMap, which is complicated
615+
pub section_number: u16,
616+
/// Index into name table, or 0xffff
617+
pub seg_name_index: u16,
618+
/// Index into name table, or 0xffff
619+
pub class_name_index: u16,
620+
/// RVA offset of this section
621+
pub rva_offset: u32,
622+
/// Length of this section
623+
pub section_length: u32,
624+
}
625+
626+
627+
impl DBISectionMapItem {
628+
fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
629+
Ok(Self {
630+
flags: buf.parse_u8()?,
631+
section_type: buf.parse_u8()?,
632+
overlay: buf.parse_u16()?,
633+
group: buf.parse_u16()?,
634+
section_number: buf.parse_u16()?,
635+
seg_name_index: buf.parse_u16()?,
636+
class_name_index: buf.parse_u16()?,
637+
rva_offset: buf.parse_u32()?,
638+
section_length: buf.parse_u32()?,
639+
})
640+
}
641+
}
642+
643+
/// A `DBISectionMapIter` iterates over the section map in the DBI section, producing `DBISectionMap`s.
644+
#[derive(Debug)]
645+
pub struct DBISectionMapIter<'c> {
646+
/// The section count.
647+
pub sec_count: u16,
648+
/// The logical section count. Typically equals sec_count, if no groups are in use. (?)
649+
pub sec_count_log: u16,
650+
buf: ParseBuffer<'c>,
651+
}
652+
653+
impl<'c> DBISectionMapIter<'c> {
654+
fn parse(mut buf: ParseBuffer<'c>) -> Result<Self> {
655+
let sec_count = buf.parse_u16()?;
656+
let sec_count_log = buf.parse_u16()?;
657+
658+
Ok(Self { buf, sec_count, sec_count_log })
659+
}
660+
}
661+
662+
impl<'c> FallibleIterator for DBISectionMapIter<'c> {
663+
type Item = DBISectionMapItem;
664+
type Error = Error;
665+
666+
fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> {
667+
// see if we're at EOF
668+
if self.buf.is_empty() {
669+
return Ok(None);
670+
}
671+
672+
let segmap = Self::Item::parse(&mut self.buf)?;
673+
Ok(Some(segmap))
674+
}
675+
}
676+
587677
/// A `DbgDataHdr`, which contains a series of (optional) MSF stream numbers.
588678
#[derive(Debug, Copy, Clone)]
589679
#[allow(dead_code)] // reason = "unused fields added for completeness"

src/pdb.rs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
// http://opensource.org/licenses/MIT>, at your option. This file may not be
66
// copied, modified, or distributed except according to those terms.
77

8-
use crate::common::*;
8+
use fallible_iterator::FallibleIterator;
9+
10+
use crate::{common::*, SectionCharacteristics};
911
use crate::dbi::{DBIExtraStreams, DBIHeader, DebugInformation, Module};
1012
use crate::framedata::FrameTable;
1113
use crate::modi::ModuleInfo;
@@ -242,7 +244,7 @@ impl<'s, S: Source<'s> + 's> PDB<'s, S> {
242244
let index = self.extra_streams()?.section_headers;
243245
let stream = match self.raw_stream(index)? {
244246
Some(stream) => stream,
245-
None => return Ok(None),
247+
None => return self.maybe_synthesize_section(),
246248
};
247249

248250
let mut buf = stream.parse_buffer();
@@ -254,6 +256,68 @@ impl<'s, S: Source<'s> + 's> PDB<'s, S> {
254256
Ok(Some(headers))
255257
}
256258

259+
// If there are no section_headers in the file, attempt to synthesize sections
260+
// based on the section map. This seems to be necessary to handle NGEN-generated PDB
261+
// files (.ni.pdb from Crossgen2).
262+
fn maybe_synthesize_section(&mut self) -> Result<Option<Vec<ImageSectionHeader>>>
263+
{
264+
// If we have OMAP From data, I don't believe we can do this, because the RVAs
265+
// won't map. But I'm not 100% sure of that, be conservative.
266+
if self.omap_from_src()?.is_some() {
267+
return Ok(None)
268+
}
269+
270+
let debug_info = self.debug_information()?;
271+
let sec_map = debug_info.section_map()?;
272+
if sec_map.sec_count != sec_map.sec_count_log {
273+
return Ok(None)
274+
}
275+
let sec_map = sec_map.collect::<Vec<_>>()?;
276+
277+
let mut rva = 0x1000u32; // in the absence of explicit section data, this starts at 0x1000
278+
let sections = sec_map.into_iter()
279+
.filter(|sm| {
280+
// the section with a bogus section length also doesn't have any rwx flags,
281+
// and has section_type == 2
282+
sm.section_type == 1 && // "SEL" section, not ABS (0x2) or GROUP (0x10)
283+
sm.section_length != u32::MAX // shouldn't happen, but just in case
284+
})
285+
.map(|sm| {
286+
let mut characteristics = 0u32;
287+
if sm.flags & 0x1 != 0 { // R
288+
characteristics |= 0x40000000; // IMAGE_SCN_MEM_READ
289+
}
290+
if sm.flags & 0x2 != 0 { // W
291+
characteristics |= 0x80000000; // IMAGE_SCN_MEM_WRITE
292+
}
293+
if sm.flags & 0x4 != 0 { // X
294+
characteristics |= 0x20000000; // IMAGE_SCN_MEM_EXECUTE
295+
characteristics |= 0x20; // IMAGE_SCN_CNT_CODE
296+
}
297+
298+
if sm.rva_offset != 0 {
299+
eprintln!("pdb: synthesizing section with rva_offset != 0, might not be correct! {:?}", sm);
300+
}
301+
302+
let this_rva = rva + sm.rva_offset;
303+
rva = this_rva + sm.section_length;
304+
ImageSectionHeader {
305+
name: [0; 8],
306+
virtual_size: sm.section_length,
307+
virtual_address: this_rva,
308+
size_of_raw_data: sm.section_length,
309+
pointer_to_raw_data: 0,
310+
pointer_to_relocations: 0,
311+
pointer_to_line_numbers: 0,
312+
number_of_relocations: 0,
313+
number_of_line_numbers: 0,
314+
characteristics: SectionCharacteristics(characteristics),
315+
}
316+
}).collect::<Vec<_>>();
317+
318+
Ok(Some(sections))
319+
}
320+
257321
/// Retrieve the global frame data table.
258322
///
259323
/// This table describes the stack frame layout for functions from all modules in the PDB. Not

0 commit comments

Comments
 (0)