Skip to content

Commit edecc6f

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

File tree

2 files changed

+158
-2
lines changed

2 files changed

+158
-2
lines changed

src/dbi.rs

Lines changed: 93 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,86 @@ 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+
impl DBISectionMapItem {
627+
fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
628+
Ok(Self {
629+
flags: buf.parse_u8()?,
630+
section_type: buf.parse_u8()?,
631+
overlay: buf.parse_u16()?,
632+
group: buf.parse_u16()?,
633+
section_number: buf.parse_u16()?,
634+
seg_name_index: buf.parse_u16()?,
635+
class_name_index: buf.parse_u16()?,
636+
rva_offset: buf.parse_u32()?,
637+
section_length: buf.parse_u32()?,
638+
})
639+
}
640+
}
641+
642+
/// A `DBISectionMapIter` iterates over the section map in the DBI section, producing `DBISectionMap`s.
643+
#[derive(Debug)]
644+
pub struct DBISectionMapIter<'c> {
645+
/// The section count.
646+
pub sec_count: u16,
647+
/// The logical section count. Typically equals sec_count, if no groups are in use. (?)
648+
pub sec_count_log: u16,
649+
buf: ParseBuffer<'c>,
650+
}
651+
652+
impl<'c> DBISectionMapIter<'c> {
653+
fn parse(mut buf: ParseBuffer<'c>) -> Result<Self> {
654+
let sec_count = buf.parse_u16()?;
655+
let sec_count_log = buf.parse_u16()?;
656+
657+
Ok(Self {
658+
buf,
659+
sec_count,
660+
sec_count_log,
661+
})
662+
}
663+
}
664+
665+
impl<'c> FallibleIterator for DBISectionMapIter<'c> {
666+
type Item = DBISectionMapItem;
667+
type Error = Error;
668+
669+
fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> {
670+
// see if we're at EOF
671+
if self.buf.is_empty() {
672+
return Ok(None);
673+
}
674+
675+
let segmap = Self::Item::parse(&mut self.buf)?;
676+
Ok(Some(segmap))
677+
}
678+
}
679+
587680
/// A `DbgDataHdr`, which contains a series of (optional) MSF stream numbers.
588681
#[derive(Debug, Copy, Clone)]
589682
#[allow(dead_code)] // reason = "unused fields added for completeness"

src/pdb.rs

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
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+
910
use crate::dbi::{DBIExtraStreams, DBIHeader, DebugInformation, Module};
1011
use crate::framedata::FrameTable;
1112
use crate::modi::ModuleInfo;
@@ -17,6 +18,7 @@ use crate::source::Source;
1718
use crate::strings::StringTable;
1819
use crate::symbol::SymbolTable;
1920
use crate::tpi::{IdInformation, TypeInformation};
21+
use crate::{common::*, SectionCharacteristics};
2022

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

0 commit comments

Comments
 (0)