use std::fmt;
use scroll::{ctx::TryFromCtx, Endian, Pread, LE};
use crate::common::*;
use crate::msf::*;
use crate::FallibleIterator;
mod annotations;
mod constants;
use self::constants::*;
pub use self::constants::{CPUType, SourceLanguage};
pub use self::annotations::*;
pub type SymbolKind = u16;
#[derive(Copy, Clone, PartialEq)]
pub struct Symbol<'t> {
index: SymbolIndex,
data: &'t [u8],
}
impl<'t> Symbol<'t> {
#[inline]
pub fn index(&self) -> SymbolIndex {
self.index
}
#[inline]
pub fn raw_kind(&self) -> SymbolKind {
debug_assert!(self.data.len() >= 2);
self.data.pread_with(0, LE).unwrap_or_default()
}
#[inline]
pub fn raw_bytes(&self) -> &'t [u8] {
self.data
}
#[inline]
pub fn parse(&self) -> Result<SymbolData<'t>> {
Ok(self.raw_bytes().pread_with(0, ())?)
}
pub fn starts_scope(&self) -> bool {
matches!(
self.raw_kind(),
S_GPROC16
| S_GPROC32
| S_GPROC32_ST
| S_GPROCMIPS
| S_GPROCMIPS_ST
| S_GPROCIA64
| S_GPROCIA64_ST
| S_LPROC16
| S_LPROC32
| S_LPROC32_ST
| S_LPROC32_DPC
| S_LPROCMIPS
| S_LPROCMIPS_ST
| S_LPROCIA64
| S_LPROCIA64_ST
| S_LPROC32_DPC_ID
| S_GPROC32_ID
| S_GPROCMIPS_ID
| S_GPROCIA64_ID
| S_BLOCK16
| S_BLOCK32
| S_BLOCK32_ST
| S_WITH16
| S_WITH32
| S_WITH32_ST
| S_THUNK16
| S_THUNK32
| S_THUNK32_ST
| S_SEPCODE
| S_GMANPROC
| S_GMANPROC_ST
| S_LMANPROC
| S_LMANPROC_ST
| S_INLINESITE
| S_INLINESITE2
)
}
pub fn ends_scope(&self) -> bool {
matches!(self.raw_kind(), S_END | S_PROC_ID_END | S_INLINESITE_END)
}
}
impl<'t> fmt::Debug for Symbol<'t> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Symbol{{ kind: 0x{:x} [{} bytes] }}",
self.raw_kind(),
self.data.len()
)
}
}
fn parse_symbol_name<'t>(buf: &mut ParseBuffer<'t>, kind: SymbolKind) -> Result<RawString<'t>> {
if kind < S_ST_MAX {
buf.parse_u8_pascal_string()
} else {
buf.parse_cstring()
}
}
fn parse_optional_name<'t>(
buf: &mut ParseBuffer<'t>,
kind: SymbolKind,
) -> Result<Option<RawString<'t>>> {
if kind < S_ST_MAX {
Ok(None)
} else {
buf.parse_cstring().map(Some)
}
}
fn parse_optional_index(buf: &mut ParseBuffer<'_>) -> Result<Option<SymbolIndex>> {
Ok(match buf.parse()? {
SymbolIndex(0) => None,
index => Some(index),
})
}
#[non_exhaustive]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SymbolData<'t> {
ScopeEnd,
ObjName(ObjNameSymbol<'t>),
RegisterVariable(RegisterVariableSymbol<'t>),
Constant(ConstantSymbol<'t>),
UserDefinedType(UserDefinedTypeSymbol<'t>),
MultiRegisterVariable(MultiRegisterVariableSymbol<'t>),
Data(DataSymbol<'t>),
Public(PublicSymbol<'t>),
Procedure(ProcedureSymbol<'t>),
ThreadStorage(ThreadStorageSymbol<'t>),
CompileFlags(CompileFlagsSymbol<'t>),
UsingNamespace(UsingNamespaceSymbol<'t>),
ProcedureReference(ProcedureReferenceSymbol<'t>),
DataReference(DataReferenceSymbol<'t>),
AnnotationReference(AnnotationReferenceSymbol<'t>),
Export(ExportSymbol<'t>),
Local(LocalSymbol<'t>),
BuildInfo(BuildInfoSymbol),
InlineSite(InlineSiteSymbol<'t>),
InlineSiteEnd,
ProcedureEnd,
Label(LabelSymbol<'t>),
Block(BlockSymbol<'t>),
RegisterRelative(RegisterRelativeSymbol<'t>),
Thunk(ThunkSymbol<'t>),
SeparatedCode(SeparatedCodeSymbol),
}
impl<'t> SymbolData<'t> {
pub fn name(&self) -> Option<RawString<'t>> {
match self {
SymbolData::ScopeEnd => None,
SymbolData::ObjName(data) => Some(data.name),
SymbolData::RegisterVariable(_) => None,
SymbolData::Constant(data) => Some(data.name),
SymbolData::UserDefinedType(data) => Some(data.name),
SymbolData::MultiRegisterVariable(_) => None,
SymbolData::Data(data) => Some(data.name),
SymbolData::Public(data) => Some(data.name),
SymbolData::Procedure(data) => Some(data.name),
SymbolData::ThreadStorage(data) => Some(data.name),
SymbolData::CompileFlags(_) => None,
SymbolData::UsingNamespace(data) => Some(data.name),
SymbolData::ProcedureReference(data) => data.name,
SymbolData::DataReference(data) => data.name,
SymbolData::AnnotationReference(data) => Some(data.name),
SymbolData::Export(data) => Some(data.name),
SymbolData::Local(data) => Some(data.name),
SymbolData::InlineSite(_) => None,
SymbolData::BuildInfo(_) => None,
SymbolData::InlineSiteEnd => None,
SymbolData::ProcedureEnd => None,
SymbolData::Label(data) => Some(data.name),
SymbolData::Block(data) => Some(data.name),
SymbolData::RegisterRelative(data) => Some(data.name),
SymbolData::Thunk(data) => Some(data.name),
SymbolData::SeparatedCode(_) => None,
}
}
}
impl<'t> TryFromCtx<'t> for SymbolData<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], _ctx: ()) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let kind = buf.parse()?;
let symbol = match kind {
S_END => SymbolData::ScopeEnd,
S_OBJNAME | S_OBJNAME_ST => SymbolData::ObjName(buf.parse_with(kind)?),
S_REGISTER | S_REGISTER_ST => SymbolData::RegisterVariable(buf.parse_with(kind)?),
S_CONSTANT | S_CONSTANT_ST | S_MANCONSTANT => {
SymbolData::Constant(buf.parse_with(kind)?)
}
S_UDT | S_UDT_ST | S_COBOLUDT | S_COBOLUDT_ST => {
SymbolData::UserDefinedType(buf.parse_with(kind)?)
}
S_MANYREG | S_MANYREG_ST | S_MANYREG2 | S_MANYREG2_ST => {
SymbolData::MultiRegisterVariable(buf.parse_with(kind)?)
}
S_LDATA32 | S_LDATA32_ST | S_GDATA32 | S_GDATA32_ST | S_LMANDATA | S_LMANDATA_ST
| S_GMANDATA | S_GMANDATA_ST => SymbolData::Data(buf.parse_with(kind)?),
S_PUB32 | S_PUB32_ST => SymbolData::Public(buf.parse_with(kind)?),
S_LPROC32 | S_LPROC32_ST | S_GPROC32 | S_GPROC32_ST | S_LPROC32_ID | S_GPROC32_ID
| S_LPROC32_DPC | S_LPROC32_DPC_ID => SymbolData::Procedure(buf.parse_with(kind)?),
S_LTHREAD32 | S_LTHREAD32_ST | S_GTHREAD32 | S_GTHREAD32_ST => {
SymbolData::ThreadStorage(buf.parse_with(kind)?)
}
S_COMPILE2 | S_COMPILE2_ST | S_COMPILE3 => {
SymbolData::CompileFlags(buf.parse_with(kind)?)
}
S_UNAMESPACE | S_UNAMESPACE_ST => SymbolData::UsingNamespace(buf.parse_with(kind)?),
S_PROCREF | S_PROCREF_ST | S_LPROCREF | S_LPROCREF_ST => {
SymbolData::ProcedureReference(buf.parse_with(kind)?)
}
S_DATAREF | S_DATAREF_ST => SymbolData::DataReference(buf.parse_with(kind)?),
S_ANNOTATIONREF => SymbolData::AnnotationReference(buf.parse_with(kind)?),
S_EXPORT => SymbolData::Export(buf.parse_with(kind)?),
S_LOCAL => SymbolData::Local(buf.parse_with(kind)?),
S_BUILDINFO => SymbolData::BuildInfo(buf.parse_with(kind)?),
S_INLINESITE | S_INLINESITE2 => SymbolData::InlineSite(buf.parse_with(kind)?),
S_INLINESITE_END => SymbolData::InlineSiteEnd,
S_PROC_ID_END => SymbolData::ProcedureEnd,
S_LABEL32 | S_LABEL32_ST => SymbolData::Label(buf.parse_with(kind)?),
S_BLOCK32 | S_BLOCK32_ST => SymbolData::Block(buf.parse_with(kind)?),
S_REGREL32 => SymbolData::RegisterRelative(buf.parse_with(kind)?),
S_THUNK32 | S_THUNK32_ST => SymbolData::Thunk(buf.parse_with(kind)?),
S_SEPCODE => SymbolData::SeparatedCode(buf.parse_with(kind)?),
other => return Err(Error::UnimplementedSymbolKind(other)),
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RegisterVariableSymbol<'t> {
pub type_index: TypeIndex,
pub register: Register,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for RegisterVariableSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = RegisterVariableSymbol {
type_index: buf.parse()?,
register: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MultiRegisterVariableSymbol<'t> {
pub type_index: TypeIndex,
pub registers: Vec<(Register, RawString<'t>)>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for MultiRegisterVariableSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let type_index = buf.parse()?;
let count = match kind {
S_MANYREG2 | S_MANYREG2_ST => buf.parse::<u16>()?,
_ => u16::from(buf.parse::<u8>()?),
};
let mut registers = Vec::with_capacity(count as usize);
for _ in 0..count {
registers.push((buf.parse()?, parse_symbol_name(&mut buf, kind)?));
}
let symbol = MultiRegisterVariableSymbol {
type_index,
registers,
};
Ok((symbol, buf.pos()))
}
}
const CVPSF_CODE: u32 = 0x1;
const CVPSF_FUNCTION: u32 = 0x2;
const CVPSF_MANAGED: u32 = 0x4;
const CVPSF_MSIL: u32 = 0x8;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PublicSymbol<'t> {
pub code: bool,
pub function: bool,
pub managed: bool,
pub msil: bool,
pub offset: PdbInternalSectionOffset,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for PublicSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let flags = buf.parse::<u32>()?;
let symbol = PublicSymbol {
code: flags & CVPSF_CODE != 0,
function: flags & CVPSF_FUNCTION != 0,
managed: flags & CVPSF_MANAGED != 0,
msil: flags & CVPSF_MSIL != 0,
offset: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DataSymbol<'t> {
pub global: bool,
pub managed: bool,
pub type_index: TypeIndex,
pub offset: PdbInternalSectionOffset,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for DataSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = DataSymbol {
global: matches!(kind, S_GDATA32 | S_GDATA32_ST | S_GMANDATA | S_GMANDATA_ST),
managed: matches!(
kind,
S_LMANDATA | S_LMANDATA_ST | S_GMANDATA | S_GMANDATA_ST
),
type_index: buf.parse()?,
offset: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ProcedureReferenceSymbol<'t> {
pub global: bool,
pub sum_name: u32,
pub symbol_index: SymbolIndex,
pub module: u16,
pub name: Option<RawString<'t>>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for ProcedureReferenceSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = ProcedureReferenceSymbol {
global: matches!(kind, S_PROCREF | S_PROCREF_ST),
sum_name: buf.parse()?,
symbol_index: buf.parse()?,
module: buf.parse()?,
name: parse_optional_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DataReferenceSymbol<'t> {
pub sum_name: u32,
pub symbol_index: SymbolIndex,
pub module: u16,
pub name: Option<RawString<'t>>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for DataReferenceSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = DataReferenceSymbol {
sum_name: buf.parse()?,
symbol_index: buf.parse()?,
module: buf.parse()?,
name: parse_optional_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct AnnotationReferenceSymbol<'t> {
pub sum_name: u32,
pub symbol_index: SymbolIndex,
pub module: u16,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for AnnotationReferenceSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = AnnotationReferenceSymbol {
sum_name: buf.parse()?,
symbol_index: buf.parse()?,
module: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ConstantSymbol<'t> {
pub managed: bool,
pub type_index: TypeIndex,
pub value: Variant,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for ConstantSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = ConstantSymbol {
managed: kind == S_MANCONSTANT,
type_index: buf.parse()?,
value: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct UserDefinedTypeSymbol<'t> {
pub type_index: TypeIndex,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for UserDefinedTypeSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = UserDefinedTypeSymbol {
type_index: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ThreadStorageSymbol<'t> {
pub global: bool,
pub type_index: TypeIndex,
pub offset: PdbInternalSectionOffset,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for ThreadStorageSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = ThreadStorageSymbol {
global: matches!(kind, S_GTHREAD32 | S_GTHREAD32_ST),
type_index: buf.parse()?,
offset: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
const CV_PFLAG_NOFPO: u8 = 0x01;
const CV_PFLAG_INT: u8 = 0x02;
const CV_PFLAG_FAR: u8 = 0x04;
const CV_PFLAG_NEVER: u8 = 0x08;
const CV_PFLAG_NOTREACHED: u8 = 0x10;
const CV_PFLAG_CUST_CALL: u8 = 0x20;
const CV_PFLAG_NOINLINE: u8 = 0x40;
const CV_PFLAG_OPTDBGINFO: u8 = 0x80;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ProcedureFlags {
pub nofpo: bool,
pub int: bool,
pub far: bool,
pub never: bool,
pub notreached: bool,
pub cust_call: bool,
pub noinline: bool,
pub optdbginfo: bool,
}
impl<'t> TryFromCtx<'t, Endian> for ProcedureFlags {
type Error = scroll::Error;
fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
let (value, size) = u8::try_from_ctx(this, le)?;
let flags = ProcedureFlags {
nofpo: value & CV_PFLAG_NOFPO != 0,
int: value & CV_PFLAG_INT != 0,
far: value & CV_PFLAG_FAR != 0,
never: value & CV_PFLAG_NEVER != 0,
notreached: value & CV_PFLAG_NOTREACHED != 0,
cust_call: value & CV_PFLAG_CUST_CALL != 0,
noinline: value & CV_PFLAG_NOINLINE != 0,
optdbginfo: value & CV_PFLAG_OPTDBGINFO != 0,
};
Ok((flags, size))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ProcedureSymbol<'t> {
pub global: bool,
pub dpc: bool,
pub parent: Option<SymbolIndex>,
pub end: SymbolIndex,
pub next: Option<SymbolIndex>,
pub len: u32,
pub dbg_start_offset: u32,
pub dbg_end_offset: u32,
pub type_index: TypeIndex,
pub offset: PdbInternalSectionOffset,
pub flags: ProcedureFlags,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for ProcedureSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = ProcedureSymbol {
global: matches!(kind, S_GPROC32 | S_GPROC32_ST | S_GPROC32_ID),
dpc: matches!(kind, S_LPROC32_DPC | S_LPROC32_DPC_ID),
parent: parse_optional_index(&mut buf)?,
end: buf.parse()?,
next: parse_optional_index(&mut buf)?,
len: buf.parse()?,
dbg_start_offset: buf.parse()?,
dbg_end_offset: buf.parse()?,
type_index: buf.parse()?,
offset: buf.parse()?,
flags: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct InlineSiteSymbol<'t> {
pub parent: Option<SymbolIndex>,
pub end: SymbolIndex,
pub inlinee: IdIndex,
pub invocations: Option<u32>,
pub annotations: BinaryAnnotations<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for InlineSiteSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = InlineSiteSymbol {
parent: parse_optional_index(&mut buf)?,
end: buf.parse()?,
inlinee: buf.parse()?,
invocations: match kind {
S_INLINESITE2 => Some(buf.parse()?),
_ => None,
},
annotations: BinaryAnnotations::new(buf.take(buf.len())?),
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BuildInfoSymbol {
pub id: IdIndex,
}
impl<'t> TryFromCtx<'t, SymbolKind> for BuildInfoSymbol {
type Error = Error;
fn try_from_ctx(this: &'t [u8], _kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = BuildInfoSymbol { id: buf.parse()? };
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ObjNameSymbol<'t> {
pub signature: u32,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for ObjNameSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = ObjNameSymbol {
signature: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CompilerVersion {
pub major: u16,
pub minor: u16,
pub build: u16,
pub qfe: Option<u16>,
}
impl<'t> TryFromCtx<'t, bool> for CompilerVersion {
type Error = Error;
fn try_from_ctx(this: &'t [u8], has_qfe: bool) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let version = CompilerVersion {
major: buf.parse()?,
minor: buf.parse()?,
build: buf.parse()?,
qfe: if has_qfe { Some(buf.parse()?) } else { None },
};
Ok((version, buf.pos()))
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CompileFlags {
pub edit_and_continue: bool,
pub no_debug_info: bool,
pub link_time_codegen: bool,
pub no_data_align: bool,
pub managed: bool,
pub security_checks: bool,
pub hot_patch: bool,
pub cvtcil: bool,
pub msil_module: bool,
pub sdl: bool,
pub pgo: bool,
pub exp_module: bool,
}
impl<'t> TryFromCtx<'t, SymbolKind> for CompileFlags {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let is_compile3 = kind == S_COMPILE3;
let raw = this.pread_with::<u16>(0, LE)?;
this.pread::<u8>(2)?;
let flags = CompileFlags {
edit_and_continue: raw & 1 != 0,
no_debug_info: (raw >> 1) & 1 != 0,
link_time_codegen: (raw >> 2) & 1 != 0,
no_data_align: (raw >> 3) & 1 != 0,
managed: (raw >> 4) & 1 != 0,
security_checks: (raw >> 5) & 1 != 0,
hot_patch: (raw >> 6) & 1 != 0,
cvtcil: (raw >> 7) & 1 != 0,
msil_module: (raw >> 8) & 1 != 0,
sdl: (raw >> 9) & 1 != 0 && is_compile3,
pgo: (raw >> 10) & 1 != 0 && is_compile3,
exp_module: (raw >> 11) & 1 != 0 && is_compile3,
};
Ok((flags, 3))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct CompileFlagsSymbol<'t> {
pub language: SourceLanguage,
pub flags: CompileFlags,
pub cpu_type: CPUType,
pub frontend_version: CompilerVersion,
pub backend_version: CompilerVersion,
pub version_string: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for CompileFlagsSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let has_qfe = kind == S_COMPILE3;
let symbol = CompileFlagsSymbol {
language: buf.parse()?,
flags: buf.parse_with(kind)?,
cpu_type: buf.parse()?,
frontend_version: buf.parse_with(has_qfe)?,
backend_version: buf.parse_with(has_qfe)?,
version_string: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct UsingNamespaceSymbol<'t> {
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for UsingNamespaceSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = UsingNamespaceSymbol {
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
const CV_LVARFLAG_ISPARAM: u16 = 0x01;
const CV_LVARFLAG_ADDRTAKEN: u16 = 0x02;
const CV_LVARFLAG_COMPGENX: u16 = 0x04;
const CV_LVARFLAG_ISAGGREGATE: u16 = 0x08;
const CV_LVARFLAG_ISALIASED: u16 = 0x10;
const CV_LVARFLAG_ISALIAS: u16 = 0x20;
const CV_LVARFLAG_ISRETVALUE: u16 = 0x40;
const CV_LVARFLAG_ISOPTIMIZEDOUT: u16 = 0x80;
const CV_LVARFLAG_ISENREG_GLOB: u16 = 0x100;
const CV_LVARFLAG_ISENREG_STAT: u16 = 0x200;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LocalVariableFlags {
pub isparam: bool,
pub addrtaken: bool,
pub compgenx: bool,
pub isaggregate: bool,
pub isaliased: bool,
pub isalias: bool,
pub isretvalue: bool,
pub isoptimizedout: bool,
pub isenreg_glob: bool,
pub isenreg_stat: bool,
}
impl<'t> TryFromCtx<'t, Endian> for LocalVariableFlags {
type Error = scroll::Error;
fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
let (value, size) = u16::try_from_ctx(this, le)?;
let flags = LocalVariableFlags {
isparam: value & CV_LVARFLAG_ISPARAM != 0,
addrtaken: value & CV_LVARFLAG_ADDRTAKEN != 0,
compgenx: value & CV_LVARFLAG_COMPGENX != 0,
isaggregate: value & CV_LVARFLAG_ISAGGREGATE != 0,
isaliased: value & CV_LVARFLAG_ISALIASED != 0,
isalias: value & CV_LVARFLAG_ISALIAS != 0,
isretvalue: value & CV_LVARFLAG_ISRETVALUE != 0,
isoptimizedout: value & CV_LVARFLAG_ISOPTIMIZEDOUT != 0,
isenreg_glob: value & CV_LVARFLAG_ISENREG_GLOB != 0,
isenreg_stat: value & CV_LVARFLAG_ISENREG_STAT != 0,
};
Ok((flags, size))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LocalSymbol<'t> {
pub type_index: TypeIndex,
pub flags: LocalVariableFlags,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for LocalSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = LocalSymbol {
type_index: buf.parse()?,
flags: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ExportSymbolFlags {
pub constant: bool,
pub data: bool,
pub private: bool,
pub no_name: bool,
pub ordinal: bool,
pub forwarder: bool,
}
impl<'t> TryFromCtx<'t, Endian> for ExportSymbolFlags {
type Error = scroll::Error;
fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
let (value, size) = u16::try_from_ctx(this, le)?;
let flags = ExportSymbolFlags {
constant: value & 0x01 != 0,
data: value & 0x02 != 0,
private: value & 0x04 != 0,
no_name: value & 0x08 != 0,
ordinal: value & 0x10 != 0,
forwarder: value & 0x20 != 0,
};
Ok((flags, size))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ExportSymbol<'t> {
pub ordinal: u16,
pub flags: ExportSymbolFlags,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for ExportSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = ExportSymbol {
ordinal: buf.parse()?,
flags: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LabelSymbol<'t> {
pub offset: PdbInternalSectionOffset,
pub flags: ProcedureFlags,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for LabelSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = LabelSymbol {
offset: buf.parse()?,
flags: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BlockSymbol<'t> {
pub parent: SymbolIndex,
pub end: SymbolIndex,
pub len: u32,
pub offset: PdbInternalSectionOffset,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for BlockSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = BlockSymbol {
parent: buf.parse()?,
end: buf.parse()?,
len: buf.parse()?,
offset: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RegisterRelativeSymbol<'t> {
pub offset: i32,
pub type_index: TypeIndex,
pub register: Register,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for RegisterRelativeSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let symbol = RegisterRelativeSymbol {
offset: buf.parse()?,
type_index: buf.parse()?,
register: buf.parse()?,
name: parse_symbol_name(&mut buf, kind)?,
};
Ok((symbol, buf.pos()))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ThunkAdjustor<'t> {
delta: u16,
target: RawString<'t>,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ThunkKind<'t> {
NoType,
Adjustor(ThunkAdjustor<'t>),
VCall(u16),
PCode,
Load,
Unknown(u8),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ThunkSymbol<'t> {
pub parent: Option<SymbolIndex>,
pub end: SymbolIndex,
pub next: Option<SymbolIndex>,
pub offset: PdbInternalSectionOffset,
pub len: u16,
pub kind: ThunkKind<'t>,
pub name: RawString<'t>,
}
impl<'t> TryFromCtx<'t, SymbolKind> for ThunkSymbol<'t> {
type Error = Error;
fn try_from_ctx(this: &'t [u8], kind: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let parent = parse_optional_index(&mut buf)?;
let end = buf.parse()?;
let next = parse_optional_index(&mut buf)?;
let offset = buf.parse()?;
let len = buf.parse()?;
let ord = buf.parse::<u8>()?;
let name = parse_symbol_name(&mut buf, kind)?;
let kind = match ord {
0 => ThunkKind::NoType,
1 => ThunkKind::Adjustor(ThunkAdjustor {
delta: buf.parse::<u16>()?,
target: buf.parse_cstring()?,
}),
2 => ThunkKind::VCall(buf.parse::<u16>()?),
3 => ThunkKind::PCode,
4 => ThunkKind::Load,
ord => ThunkKind::Unknown(ord),
};
let symbol = ThunkSymbol {
parent,
end,
next,
offset,
len,
kind,
name,
};
Ok((symbol, buf.pos()))
}
}
const CV_SEPCODEFLAG_IS_LEXICAL_SCOPE: u32 = 0x01;
const CV_SEPCODEFLAG_RETURNS_TO_PARENT: u32 = 0x02;
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SeparatedCodeFlags {
pub islexicalscope: bool,
pub returnstoparent: bool,
}
impl<'t> TryFromCtx<'t, Endian> for SeparatedCodeFlags {
type Error = scroll::Error;
fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
let (value, size) = u32::try_from_ctx(this, le)?;
let flags = SeparatedCodeFlags {
islexicalscope: value & CV_SEPCODEFLAG_IS_LEXICAL_SCOPE != 0,
returnstoparent: value & CV_SEPCODEFLAG_RETURNS_TO_PARENT != 0,
};
Ok((flags, size))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SeparatedCodeSymbol {
pub parent: SymbolIndex,
pub end: SymbolIndex,
pub len: u32,
pub flags: SeparatedCodeFlags,
pub offset: PdbInternalSectionOffset,
pub parent_offset: PdbInternalSectionOffset,
}
impl<'t> TryFromCtx<'t, SymbolKind> for SeparatedCodeSymbol {
type Error = Error;
fn try_from_ctx(this: &'t [u8], _: SymbolKind) -> Result<(Self, usize)> {
let mut buf = ParseBuffer::from(this);
let parent = buf.parse()?;
let end = buf.parse()?;
let len = buf.parse()?;
let flags = buf.parse()?;
let offset = buf.parse()?;
let parent_offset = buf.parse()?;
let section = buf.parse()?;
let parent_section = buf.parse()?;
let symbol = SeparatedCodeSymbol {
parent,
end,
len,
flags,
offset: PdbInternalSectionOffset { offset, section },
parent_offset: PdbInternalSectionOffset {
offset: parent_offset,
section: parent_section,
},
};
Ok((symbol, buf.pos()))
}
}
#[derive(Debug)]
pub struct SymbolTable<'s> {
stream: Stream<'s>,
}
impl<'s> SymbolTable<'s> {
pub(crate) fn new(stream: Stream<'s>) -> Self {
SymbolTable { stream }
}
pub fn iter(&self) -> SymbolIter<'_> {
SymbolIter::new(self.stream.parse_buffer())
}
pub fn iter_at(&self, index: SymbolIndex) -> SymbolIter<'_> {
let mut iter = self.iter();
iter.seek(index);
iter
}
}
#[derive(Debug)]
pub struct SymbolIter<'t> {
buf: ParseBuffer<'t>,
}
impl<'t> SymbolIter<'t> {
pub(crate) fn new(buf: ParseBuffer<'t>) -> SymbolIter<'t> {
SymbolIter { buf }
}
pub fn seek(&mut self, index: SymbolIndex) {
self.buf.seek(index.0 as usize);
}
pub fn skip_to(&mut self, index: SymbolIndex) -> Result<Option<Symbol<'t>>> {
self.seek(index);
self.next()
}
}
impl<'t> FallibleIterator for SymbolIter<'t> {
type Item = Symbol<'t>;
type Error = Error;
fn next(&mut self) -> Result<Option<Self::Item>> {
while !self.buf.is_empty() {
let index = SymbolIndex(self.buf.pos() as u32);
let symbol_length = self.buf.parse::<u16>()? as usize;
if symbol_length < 2 {
return Err(Error::SymbolTooShort);
}
let data = self.buf.take(symbol_length)?;
let symbol = Symbol { index, data };
match symbol.raw_kind() {
S_ALIGN | S_SKIP => continue,
_ => return Ok(Some(symbol)),
}
}
Ok(None)
}
}
#[cfg(test)]
mod tests {
mod parsing {
use crate::symbol::*;
#[test]
fn kind_0006() {
let data = &[6, 0];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x0006);
assert_eq!(symbol.parse().expect("parse"), SymbolData::ScopeEnd);
}
#[test]
fn kind_1101() {
let data = &[1, 17, 0, 0, 0, 0, 42, 32, 67, 73, 76, 32, 42, 0];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1101);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::ObjName(ObjNameSymbol {
signature: 0,
name: "* CIL *".into(),
})
);
}
#[test]
fn kind_1102() {
let data = &[
2, 17, 0, 0, 0, 0, 108, 22, 0, 0, 0, 0, 0, 0, 140, 11, 0, 0, 1, 0, 9, 0, 3, 91,
116, 104, 117, 110, 107, 93, 58, 68, 101, 114, 105, 118, 101, 100, 58, 58, 70, 117,
110, 99, 49, 96, 97, 100, 106, 117, 115, 116, 111, 114, 123, 56, 125, 39, 0, 0, 0,
0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1102);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::Thunk(ThunkSymbol {
parent: None,
end: SymbolIndex(0x166c),
next: None,
offset: PdbInternalSectionOffset {
section: 0x1,
offset: 0xb8c
},
len: 9,
kind: ThunkKind::PCode,
name: "[thunk]:Derived::Func1`adjustor{8}'".into()
})
);
}
#[test]
fn kind_1105() {
let data = &[
5, 17, 224, 95, 151, 0, 1, 0, 0, 100, 97, 118, 49, 100, 95, 119, 95, 97, 118, 103,
95, 115, 115, 115, 101, 51, 0, 0, 0, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1105);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::Label(LabelSymbol {
offset: PdbInternalSectionOffset {
offset: 0x0097_5fe0,
section: 1
},
flags: ProcedureFlags {
nofpo: false,
int: false,
far: false,
never: false,
notreached: false,
cust_call: false,
noinline: false,
optdbginfo: false
},
name: "dav1d_w_avg_ssse3".into(),
})
);
}
#[test]
fn kind_1106() {
let data = &[6, 17, 120, 34, 0, 0, 18, 0, 116, 104, 105, 115, 0, 0];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1106);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::RegisterVariable(RegisterVariableSymbol {
type_index: TypeIndex(8824),
register: Register(18),
name: "this".into(),
})
);
}
#[test]
fn kind_110e() {
let data = &[
14, 17, 2, 0, 0, 0, 192, 85, 0, 0, 1, 0, 95, 95, 108, 111, 99, 97, 108, 95, 115,
116, 100, 105, 111, 95, 112, 114, 105, 110, 116, 102, 95, 111, 112, 116, 105, 111,
110, 115, 0, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x110e);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::Public(PublicSymbol {
code: false,
function: true,
managed: false,
msil: false,
offset: PdbInternalSectionOffset {
offset: 21952,
section: 1
},
name: "__local_stdio_printf_options".into(),
})
);
}
#[test]
fn kind_1111() {
let data = &[
17, 17, 12, 0, 0, 0, 48, 16, 0, 0, 22, 0, 109, 97, 120, 105, 109, 117, 109, 95, 99,
111, 117, 110, 116, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1111);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::RegisterRelative(RegisterRelativeSymbol {
offset: 12,
type_index: TypeIndex(0x1030),
register: Register(22),
name: "maximum_count".into(),
})
);
}
#[test]
fn kind_1124() {
let data = &[36, 17, 115, 116, 100, 0];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1124);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::UsingNamespace(UsingNamespaceSymbol { name: "std".into() })
);
}
#[test]
fn kind_1125() {
let data = &[
37, 17, 0, 0, 0, 0, 108, 0, 0, 0, 1, 0, 66, 97, 122, 58, 58, 102, 95, 112, 117, 98,
108, 105, 99, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1125);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::ProcedureReference(ProcedureReferenceSymbol {
global: true,
sum_name: 0,
symbol_index: SymbolIndex(108),
module: 1,
name: Some("Baz::f_public".into()),
})
);
}
#[test]
fn kind_1108() {
let data = &[8, 17, 112, 6, 0, 0, 118, 97, 95, 108, 105, 115, 116, 0];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1108);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::UserDefinedType(UserDefinedTypeSymbol {
type_index: TypeIndex(1648),
name: "va_list".into(),
})
);
}
#[test]
fn kind_1107() {
let data = &[
7, 17, 201, 18, 0, 0, 1, 0, 95, 95, 73, 83, 65, 95, 65, 86, 65, 73, 76, 65, 66, 76,
69, 95, 83, 83, 69, 50, 0, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1107);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::Constant(ConstantSymbol {
managed: false,
type_index: TypeIndex(4809),
value: Variant::U16(1),
name: "__ISA_AVAILABLE_SSE2".into(),
})
);
}
#[test]
fn kind_110d() {
let data = &[
13, 17, 116, 0, 0, 0, 16, 0, 0, 0, 3, 0, 95, 95, 105, 115, 97, 95, 97, 118, 97,
105, 108, 97, 98, 108, 101, 0, 0, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x110d);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::Data(DataSymbol {
global: true,
managed: false,
type_index: TypeIndex(116),
offset: PdbInternalSectionOffset {
offset: 16,
section: 3
},
name: "__isa_available".into(),
})
);
}
#[test]
fn kind_110c() {
let data = &[
12, 17, 32, 0, 0, 0, 240, 36, 1, 0, 2, 0, 36, 120, 100, 97, 116, 97, 115, 121, 109,
0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x110c);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::Data(DataSymbol {
global: false,
managed: false,
type_index: TypeIndex(32),
offset: PdbInternalSectionOffset {
offset: 74992,
section: 2
},
name: "$xdatasym".into(),
})
);
}
#[test]
fn kind_1127() {
let data = &[
39, 17, 0, 0, 0, 0, 128, 4, 0, 0, 182, 0, 99, 97, 112, 116, 117, 114, 101, 95, 99,
117, 114, 114, 101, 110, 116, 95, 99, 111, 110, 116, 101, 120, 116, 0, 0, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1127);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::ProcedureReference(ProcedureReferenceSymbol {
global: false,
sum_name: 0,
symbol_index: SymbolIndex(1152),
module: 182,
name: Some("capture_current_context".into()),
})
);
}
#[test]
fn kind_1110() {
let data = &[
16, 17, 0, 0, 0, 0, 48, 2, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 5, 0, 0, 0, 5, 0, 0, 0, 7,
16, 0, 0, 64, 85, 0, 0, 1, 0, 0, 66, 97, 122, 58, 58, 102, 95, 112, 114, 111, 116,
101, 99, 116, 101, 100, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1110);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::Procedure(ProcedureSymbol {
global: true,
dpc: false,
parent: None,
end: SymbolIndex(560),
next: None,
len: 6,
dbg_start_offset: 5,
dbg_end_offset: 5,
type_index: TypeIndex(4103),
offset: PdbInternalSectionOffset {
offset: 21824,
section: 1
},
flags: ProcedureFlags {
nofpo: false,
int: false,
far: false,
never: false,
notreached: false,
cust_call: false,
noinline: false,
optdbginfo: false
},
name: "Baz::f_protected".into(),
})
);
}
#[test]
fn kind_1103() {
let data = &[
3, 17, 244, 149, 9, 0, 40, 151, 9, 0, 135, 1, 0, 0, 108, 191, 184, 2, 1, 0, 0, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1103);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::Block(BlockSymbol {
parent: SymbolIndex(0x0009_95f4),
end: SymbolIndex(0x0009_9728),
len: 391,
offset: PdbInternalSectionOffset {
section: 0x1,
offset: 0x02b8_bf6c
},
name: "".into(),
})
);
}
#[test]
fn kind_110f() {
let data = &[
15, 17, 0, 0, 0, 0, 156, 1, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0,
128, 16, 0, 0, 196, 87, 0, 0, 1, 0, 128, 95, 95, 115, 99, 114, 116, 95, 99, 111,
109, 109, 111, 110, 95, 109, 97, 105, 110, 0, 0, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x110f);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::Procedure(ProcedureSymbol {
global: false,
dpc: false,
parent: None,
end: SymbolIndex(412),
next: None,
len: 18,
dbg_start_offset: 4,
dbg_end_offset: 9,
type_index: TypeIndex(4224),
offset: PdbInternalSectionOffset {
offset: 22468,
section: 1
},
flags: ProcedureFlags {
nofpo: false,
int: false,
far: false,
never: false,
notreached: false,
cust_call: false,
noinline: false,
optdbginfo: true
},
name: "__scrt_common_main".into(),
})
);
}
#[test]
fn kind_1116() {
let data = &[
22, 17, 7, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 14, 0, 10, 0, 115, 98, 77, 105, 99,
114, 111, 115, 111, 102, 116, 32, 40, 82, 41, 32, 76, 73, 78, 75, 0, 0, 0, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1116);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::CompileFlags(CompileFlagsSymbol {
language: SourceLanguage::Link,
flags: CompileFlags {
edit_and_continue: false,
no_debug_info: false,
link_time_codegen: false,
no_data_align: false,
managed: false,
security_checks: false,
hot_patch: false,
cvtcil: false,
msil_module: false,
sdl: false,
pgo: false,
exp_module: false,
},
cpu_type: CPUType::Intel80386,
frontend_version: CompilerVersion {
major: 0,
minor: 0,
build: 0,
qfe: None,
},
backend_version: CompilerVersion {
major: 14,
minor: 10,
build: 25203,
qfe: None,
},
version_string: "Microsoft (R) LINK".into(),
})
);
}
#[test]
fn kind_1132() {
let data = &[
50, 17, 0, 0, 0, 0, 108, 0, 0, 0, 88, 0, 0, 0, 0, 0, 0, 0, 196, 252, 10, 0, 56, 67,
0, 0, 1, 0, 1, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x1132);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::SeparatedCode(SeparatedCodeSymbol {
parent: SymbolIndex(0x0),
end: SymbolIndex(0x6c),
len: 88,
flags: SeparatedCodeFlags {
islexicalscope: false,
returnstoparent: false
},
offset: PdbInternalSectionOffset {
section: 0x1,
offset: 0xafcc4
},
parent_offset: PdbInternalSectionOffset {
section: 0x1,
offset: 0x4338
}
})
);
}
#[test]
fn kind_113c() {
let data = &[
60, 17, 1, 36, 2, 0, 7, 0, 19, 0, 13, 0, 6, 102, 0, 0, 19, 0, 13, 0, 6, 102, 0, 0,
77, 105, 99, 114, 111, 115, 111, 102, 116, 32, 40, 82, 41, 32, 79, 112, 116, 105,
109, 105, 122, 105, 110, 103, 32, 67, 111, 109, 112, 105, 108, 101, 114, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x113c);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::CompileFlags(CompileFlagsSymbol {
language: SourceLanguage::Cpp,
flags: CompileFlags {
edit_and_continue: false,
no_debug_info: false,
link_time_codegen: true,
no_data_align: false,
managed: false,
security_checks: true,
hot_patch: false,
cvtcil: false,
msil_module: false,
sdl: true,
pgo: false,
exp_module: false,
},
cpu_type: CPUType::Pentium3,
frontend_version: CompilerVersion {
major: 19,
minor: 13,
build: 26118,
qfe: Some(0),
},
backend_version: CompilerVersion {
major: 19,
minor: 13,
build: 26118,
qfe: Some(0),
},
version_string: "Microsoft (R) Optimizing Compiler".into(),
})
);
}
#[test]
fn kind_113e() {
let data = &[62, 17, 193, 19, 0, 0, 1, 0, 116, 104, 105, 115, 0, 0];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x113e);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::Local(LocalSymbol {
type_index: TypeIndex(5057),
flags: LocalVariableFlags {
isparam: true,
addrtaken: false,
compgenx: false,
isaggregate: false,
isaliased: false,
isalias: false,
isretvalue: false,
isoptimizedout: false,
isenreg_glob: false,
isenreg_stat: false,
},
name: "this".into(),
})
);
}
#[test]
fn kind_114c() {
let data = &[76, 17, 95, 17, 0, 0];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x114c);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::BuildInfo(BuildInfoSymbol {
id: IdIndex(0x115F)
})
);
}
#[test]
fn kind_114d() {
let data = &[
77, 17, 144, 1, 0, 0, 208, 1, 0, 0, 121, 17, 0, 0, 12, 6, 3, 0,
];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x114d);
assert_eq!(
symbol.parse().expect("parse"),
SymbolData::InlineSite(InlineSiteSymbol {
parent: Some(SymbolIndex(0x0190)),
end: SymbolIndex(0x01d0),
inlinee: IdIndex(4473),
invocations: None,
annotations: BinaryAnnotations::new(&[12, 6, 3, 0]),
})
);
}
#[test]
fn kind_114e() {
let data = &[78, 17];
let symbol = Symbol {
data,
index: SymbolIndex(0),
};
assert_eq!(symbol.raw_kind(), 0x114e);
assert_eq!(symbol.parse().expect("parse"), SymbolData::InlineSiteEnd);
}
}
mod iterator {
use crate::symbol::*;
fn create_iter() -> SymbolIter<'static> {
let data = &[
0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x4e, 0x11,
0x02, 0x00, 0x06, 0x00,
];
let mut buf = ParseBuffer::from(&data[..]);
buf.seek(4);
SymbolIter::new(buf)
}
#[test]
fn test_iter() {
let symbols: Vec<_> = create_iter().collect().expect("collect");
let expected = [
Symbol {
index: SymbolIndex(0x4),
data: &[0x4e, 0x11],
},
Symbol {
index: SymbolIndex(0x8),
data: &[0x06, 0x00],
},
];
assert_eq!(symbols, expected);
}
#[test]
fn test_seek() {
let mut symbols = create_iter();
symbols.seek(SymbolIndex(0x8));
let symbol = symbols.next().expect("get symbol");
let expected = Symbol {
index: SymbolIndex(0x8),
data: &[0x06, 0x00],
};
assert_eq!(symbol, Some(expected));
}
#[test]
fn test_skip_to() {
let mut symbols = create_iter();
let symbol = symbols.skip_to(SymbolIndex(0x8)).expect("get symbol");
let expected = Symbol {
index: SymbolIndex(0x8),
data: &[0x06, 0x00],
};
assert_eq!(symbol, Some(expected));
}
}
}