use std::borrow::Cow;
use std::result;
use common::*;
use msf::*;
use FallibleIterator;
#[derive(Debug)]
pub struct DebugInformation<'s> {
stream: Stream<'s>,
header: Header,
header_len: usize,
}
impl<'s> DebugInformation<'s> {
pub fn machine_type(&self) -> Result<MachineType> {
Ok(self.header.machine_type.into())
}
pub fn modules(&self) -> Result<ModuleIter> {
let mut buf = self.stream.parse_buffer();
buf.take(self.header_len)?;
let modules_buf = buf.take(self.header.module_list_size as usize)?;
Ok(ModuleIter {
buf: modules_buf.into(),
})
}
pub(crate) fn get_header(&self) -> Header {
self.header
}
pub(crate) fn new(stream: Stream) -> Result<DebugInformation> {
let (header, len) = {
let mut buf = stream.parse_buffer();
let header = parse_header(&mut buf)?;
(header, buf.pos())
};
Ok(DebugInformation {
stream: stream,
header: header,
header_len: len,
})
}
}
#[derive(Debug, Copy, Clone)]
pub enum HeaderVersion {
V41,
V50,
V60,
V70,
V110,
OtherValue(u32),
}
impl From<u32> for HeaderVersion {
fn from(v: u32) -> Self {
match v {
930803 => HeaderVersion::V41,
19960307 => HeaderVersion::V50,
19970606 => HeaderVersion::V60,
19990903 => HeaderVersion::V70,
20091201 => HeaderVersion::V110,
_ => HeaderVersion::OtherValue(v),
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct Header {
pub signature: u32,
pub version: HeaderVersion,
pub age: u32,
pub gs_symbols_stream: u16,
pub internal_version: u16,
pub ps_symbols_stream: u16,
pub pdb_dll_build_version: u16,
pub symbol_records_stream: u16,
pub pdb_dll_rbld_version: u16,
pub module_list_size: u32,
pub section_contribution_size: u32,
pub section_map_size: u32,
pub file_info_size: u32,
pub type_server_map_size: u32,
pub mfc_type_server_index: u32,
pub debug_header_size: u32,
pub ec_substream_size: u32,
pub flags: u16,
pub machine_type: u16,
pub reserved: u32,
}
pub fn parse_header(buf: &mut ParseBuffer) -> Result<Header> {
let header = Header {
signature: buf.parse_u32()?,
version: From::from(buf.parse_u32()?),
age: buf.parse_u32()?,
gs_symbols_stream: buf.parse_u16()?,
internal_version: buf.parse_u16()?,
ps_symbols_stream: buf.parse_u16()?,
pdb_dll_build_version: buf.parse_u16()?,
symbol_records_stream: buf.parse_u16()?,
pdb_dll_rbld_version: buf.parse_u16()?,
module_list_size: buf.parse_u32()?,
section_contribution_size: buf.parse_u32()?,
section_map_size: buf.parse_u32()?,
file_info_size: buf.parse_u32()?,
type_server_map_size: buf.parse_u32()?,
mfc_type_server_index: buf.parse_u32()?,
debug_header_size: buf.parse_u32()?,
ec_substream_size: buf.parse_u32()?,
flags: buf.parse_u16()?,
machine_type: buf.parse_u16()?,
reserved: buf.parse_u32()?,
};
if header.signature != 0xffffffff {
return Err(Error::UnimplementedFeature("ancient DBI header"));
}
Ok(header)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MachineType {
Unknown = 0x0,
Am33 = 0x13,
Amd64 = 0x8664,
Arm = 0x1C0,
Arm64 = 0xAA64,
ArmNT = 0x1C4,
Ebc = 0xEBC,
X86 = 0x14C,
Ia64 = 0x200,
M32R = 0x9041,
Mips16 = 0x266,
MipsFpu = 0x366,
MipsFpu16 = 0x466,
PowerPC = 0x1F0,
PowerPCFP = 0x1F1,
R4000 = 0x166,
RiscV32 = 0x5032,
RiscV64 = 0x5064,
RiscV128 = 0x5128,
SH3 = 0x1A2,
SH3DSP = 0x1A3,
SH4 = 0x1A6,
SH5 = 0x1A8,
Thumb = 0x1C2,
WceMipsV2 = 0x169,
Invalid = 0xffff,
}
impl ::std::fmt::Display for MachineType {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
MachineType::Invalid => write!(f, "Invalid"),
MachineType::Unknown => write!(f, "Unknown"),
MachineType::Am33 => write!(f, "Am33"),
MachineType::Amd64 => write!(f, "Amd64"),
MachineType::Arm => write!(f, "Arm"),
MachineType::Arm64 => write!(f, "Arm64"),
MachineType::ArmNT => write!(f, "ArmNT"),
MachineType::Ebc => write!(f, "Ebc"),
MachineType::X86 => write!(f, "X86"),
MachineType::Ia64 => write!(f, "Ia64"),
MachineType::M32R => write!(f, "M32R"),
MachineType::Mips16 => write!(f, "Mips16"),
MachineType::MipsFpu => write!(f, "MipsFpu"),
MachineType::MipsFpu16 => write!(f, "MipsFpu16"),
MachineType::PowerPC => write!(f, "PowerPC"),
MachineType::PowerPCFP => write!(f, "PowerPCFP"),
MachineType::R4000 => write!(f, "R4000"),
MachineType::RiscV32 => write!(f, "RiscV32"),
MachineType::RiscV64 => write!(f, "RiscV64"),
MachineType::RiscV128 => write!(f, "RiscV128"),
MachineType::SH3 => write!(f, "SH3"),
MachineType::SH3DSP => write!(f, "SH3DSP"),
MachineType::SH4 => write!(f, "SH4"),
MachineType::SH5 => write!(f, "SH5"),
MachineType::Thumb => write!(f, "Thumb"),
MachineType::WceMipsV2 => write!(f, "WceMipsV2"),
}
}
}
impl From<u16> for MachineType {
fn from(value: u16) -> Self {
match value {
0xffff => MachineType::Invalid,
0x0 => MachineType::Unknown,
0x13 => MachineType::Am33,
0x8664 => MachineType::Amd64,
0x1C0 => MachineType::Arm,
0xAA64 => MachineType::Arm64,
0x1C4 => MachineType::ArmNT,
0xEBC => MachineType::Ebc,
0x14C => MachineType::X86,
0x200 => MachineType::Ia64,
0x9041 => MachineType::M32R,
0x266 => MachineType::Mips16,
0x366 => MachineType::MipsFpu,
0x466 => MachineType::MipsFpu16,
0x1F0 => MachineType::PowerPC,
0x1F1 => MachineType::PowerPCFP,
0x166 => MachineType::R4000,
0x5032 => MachineType::RiscV32,
0x5064 => MachineType::RiscV64,
0x5128 => MachineType::RiscV128,
0x1A2 => MachineType::SH3,
0x1A3 => MachineType::SH3DSP,
0x1A6 => MachineType::SH4,
0x1A8 => MachineType::SH5,
0x1C2 => MachineType::Thumb,
0x169 => MachineType::WceMipsV2,
_ => MachineType::Unknown,
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct DBISectionContribution {
section: u16,
_padding1: u16,
offset: u32,
size: u32,
characteristics: u32,
module: u16,
_padding2: u16,
data_crc: u32,
reloc_crc: u32,
}
#[derive(Debug, Copy, Clone)]
pub struct DBIModuleInfo {
pub opened: u32,
pub section: DBISectionContribution,
pub flags: u16,
pub stream: u16,
pub symbols_size: u32,
pub lines_size: u32,
pub c13_lines_size: u32,
pub files: u16,
_padding: u16,
pub filename_offsets: u32,
pub source: u32,
pub compiler: u32,
}
fn parse_module_info(buf: &mut ParseBuffer) -> Result<DBIModuleInfo> {
Ok(DBIModuleInfo {
opened: buf.parse_u32()?,
section: parse_section_contribution(buf)?,
flags: buf.parse_u16()?,
stream: buf.parse_u16()?,
symbols_size: buf.parse_u32()?,
lines_size: buf.parse_u32()?,
c13_lines_size: buf.parse_u32()?,
files: buf.parse_u16()?,
_padding: buf.parse_u16()?,
filename_offsets: buf.parse_u32()?,
source: buf.parse_u32()?,
compiler: buf.parse_u32()?,
})
}
fn parse_section_contribution(buf: &mut ParseBuffer) -> Result<DBISectionContribution> {
Ok(DBISectionContribution {
section: buf.parse_u16()?,
_padding1: buf.parse_u16()?,
offset: buf.parse_u32()?,
size: buf.parse_u32()?,
characteristics: buf.parse_u32()?,
module: buf.parse_u16()?,
_padding2: buf.parse_u16()?,
data_crc: buf.parse_u32()?,
reloc_crc: buf.parse_u32()?,
})
}
#[derive(Debug, Clone)]
pub struct Module<'m> {
info: DBIModuleInfo,
module_name: RawString<'m>,
object_file_name: RawString<'m>,
}
impl<'m> Module<'m> {
pub fn info(&self) -> &DBIModuleInfo {
&self.info
}
pub fn module_name(&self) -> Cow<'m, str> {
self.module_name.to_string()
}
pub fn object_file_name(&self) -> Cow<'m, str> {
self.object_file_name.to_string()
}
}
#[derive(Debug)]
pub struct ModuleIter<'m> {
buf: ParseBuffer<'m>,
}
impl<'m> FallibleIterator for ModuleIter<'m> {
type Item = Module<'m>;
type Error = Error;
fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> {
if self.buf.len() == 0 {
return Ok(None);
}
let info = parse_module_info(&mut self.buf)?;
let module_name = self.buf.parse_cstring()?;
let object_file_name = self.buf.parse_cstring()?;
self.buf.align(4)?;
Ok(Some(Module {
info,
module_name,
object_file_name,
}))
}
}
#[derive(Debug, Copy, Clone)]
pub(crate) struct DBIExtraStreams {
fpo: u16,
exception: u16,
fixup: u16,
omap_to_src: u16,
omap_from_src: u16,
section_headers: u16,
token_rid_map: u16,
xdata: u16,
pdata: u16,
new_fpo: u16,
original_section_headers: u16,
}
fn optional_stream_number(sn: u16) -> Option<u16> {
if sn == 0xffff {
None
} else {
Some(sn)
}
}
impl DBIExtraStreams {
pub(crate) fn new(debug_info: &DebugInformation) -> Result<DBIExtraStreams> {
let header = debug_info.header;
let offset = debug_info.header_len + (
header.module_list_size
+ header.section_contribution_size
+ header.section_map_size
+ header.file_info_size
+ header.type_server_map_size
+ header.mfc_type_server_index
+ header.ec_substream_size
) as usize;
let mut buf = debug_info.stream.parse_buffer();
buf.take(offset)?;
let bytes = buf.take(header.debug_header_size as _)?;
let mut extra_streams_buf = ParseBuffer::from(bytes);
Self::parse(&mut extra_streams_buf)
}
pub(crate) fn parse(buf: &mut ParseBuffer) -> Result<DBIExtraStreams> {
if buf.len() % 2 == 1 {
return Err(Error::UnimplementedFeature("DbgDataHdr should always be an even number of bytes"))
}
fn eof_to_placeholder_stream(err: Error) -> Result<u16> {
match err {
Error::UnexpectedEof => Ok(0xffff),
other => Err(other)
}
}
Ok(DBIExtraStreams {
fpo: buf.parse_u16().or_else(eof_to_placeholder_stream)?,
exception: buf.parse_u16().or_else(eof_to_placeholder_stream)?,
fixup: buf.parse_u16().or_else(eof_to_placeholder_stream)?,
omap_to_src: buf.parse_u16().or_else(eof_to_placeholder_stream)?,
omap_from_src: buf.parse_u16().or_else(eof_to_placeholder_stream)?,
section_headers: buf.parse_u16().or_else(eof_to_placeholder_stream)?,
token_rid_map: buf.parse_u16().or_else(eof_to_placeholder_stream)?,
xdata: buf.parse_u16().or_else(eof_to_placeholder_stream)?,
pdata: buf.parse_u16().or_else(eof_to_placeholder_stream)?,
new_fpo: buf.parse_u16().or_else(eof_to_placeholder_stream)?,
original_section_headers: buf.parse_u16().or_else(eof_to_placeholder_stream)?,
})
}
pub fn fpo(&self) -> Option<u16> { optional_stream_number(self.fpo) }
pub fn exception(&self) -> Option<u16> { optional_stream_number(self.exception) }
pub fn fixup(&self) -> Option<u16> { optional_stream_number(self.fixup) }
pub fn omap_to_src(&self) -> Option<u16> { optional_stream_number(self.omap_to_src) }
pub fn omap_from_src(&self) -> Option<u16> { optional_stream_number(self.omap_from_src) }
pub fn section_headers(&self) -> Option<u16> { optional_stream_number(self.section_headers) }
pub fn token_rid_map(&self) -> Option<u16> { optional_stream_number(self.token_rid_map) }
pub fn xdata(&self) -> Option<u16> { optional_stream_number(self.xdata) }
pub fn pdata(&self) -> Option<u16> { optional_stream_number(self.pdata) }
pub fn new_fpo(&self) -> Option<u16> { optional_stream_number(self.new_fpo) }
pub fn original_section_headers(&self) -> Option<u16> { optional_stream_number(self.original_section_headers) }
}
#[cfg(test)]
mod tests {
use dbi::*;
#[test]
fn test_dbi_extra_streams() {
let bytes = vec![
0xff, 0xff,
0x01, 0x02,
0x03, 0x04,
0xff, 0xff,
0x05, 0x06
];
let mut buf = ParseBuffer::from(bytes.as_slice());
let extra_streams = DBIExtraStreams::parse(&mut buf).expect("parse");
assert_eq!(extra_streams.fpo(), None);
assert_eq!(extra_streams.exception(), Some(0x0201));
assert_eq!(extra_streams.fixup(), Some(0x0403));
assert_eq!(extra_streams.omap_to_src(), None);
assert_eq!(extra_streams.omap_from_src(), Some(0x0605));
assert_eq!(extra_streams.section_headers(), None);
assert_eq!(extra_streams.token_rid_map(), None);
assert_eq!(extra_streams.original_section_headers(), None);
}
}