use std::fmt;
use std::result;
use FallibleIterator;
use common::*;
use msf::*;
mod constants;
use self::constants::*;
#[derive(Debug)]
pub struct SymbolTable<'t> {
stream: Stream<'t>,
}
pub(crate) fn new_symbol_table(s: Stream) -> SymbolTable {
SymbolTable{
stream: s,
}
}
impl<'t> SymbolTable<'t> {
pub fn iter(&self) -> SymbolIter {
SymbolIter::new(self.stream.parse_buffer())
}
}
#[derive(Copy,Clone,PartialEq)]
pub struct Symbol<'t>(&'t [u8]);
impl<'t> Symbol<'t> {
#[inline]
pub fn raw_kind(&self) -> u16 {
debug_assert!(self.0.len() >= 2);
(self.0[0] as u16) | ((self.0[1] as u16) << 8)
}
pub fn raw_bytes(&self) -> &'t [u8] {
self.0
}
fn data_length(&self) -> Result<usize> {
let kind = self.raw_kind();
let data_length = match kind {
S_PUB32 | S_PUB32_ST => 10,
S_LDATA32 | S_LDATA32_ST |
S_GDATA32 | S_GDATA32_ST |
S_LMANDATA | S_LMANDATA_ST |
S_GMANDATA | S_GMANDATA_ST => 10,
S_PROCREF | S_PROCREF_ST |
S_LPROCREF | S_LPROCREF_ST |
S_DATAREF | S_DATAREF_ST |
S_ANNOTATIONREF => 10,
S_CONSTANT | S_CONSTANT_ST => {
let mut constant_size = 6;
let mut buf = ParseBuffer::from(&self.0[2 + 4 ..]);
let leaf = buf.parse_u16()?;
if leaf >= ::tpi::constants::LF_NUMERIC {
match leaf {
::tpi::constants::LF_CHAR => { constant_size += 1; },
::tpi::constants::LF_SHORT => { constant_size += 2; },
::tpi::constants::LF_LONG => { constant_size += 4; },
::tpi::constants::LF_QUADWORD => { constant_size += 8; },
::tpi::constants::LF_USHORT => { constant_size += 2; },
::tpi::constants::LF_ULONG => { constant_size += 4; },
::tpi::constants::LF_UQUADWORD => { constant_size += 8; },
_ => {
debug_assert!(false);
}
}
}
constant_size
},
S_UDT | S_UDT_ST => 4,
S_LTHREAD32 | S_LTHREAD32_ST |
S_GTHREAD32 | S_GTHREAD32_ST => 10,
S_LPROC32 | S_LPROC32_ST |
S_GPROC32 | S_GPROC32_ST |
S_LPROC32_ID |
S_GPROC32_ID |
S_LPROC32_DPC |
S_LPROC32_DPC_ID => 35,
S_OBJNAME | S_OBJNAME_ST => 4,
S_COMPILE3 => 22,
S_UNAMESPACE | S_UNAMESPACE_ST => 0,
_ => return Err(Error::UnimplementedSymbolKind(kind))
};
if self.0.len() < data_length + 2 {
return Err(Error::SymbolTooShort);
}
Ok(data_length)
}
#[inline]
pub fn parse(&self) -> Result<SymbolData> {
parse_symbol_data(self.raw_kind(), self.field_data()?)
}
fn field_data(&self) -> Result<&'t [u8]> {
let data_length = self.data_length()?;
Ok(&self.0[2..(data_length+2)])
}
pub fn name(&self) -> Result<RawString<'t>> {
let data_length = self.data_length()?;
let mut buf = ParseBuffer::from(&self.0[2 + data_length ..]);
if self.raw_kind() < S_ST_MAX {
let name = buf.parse_u8_pascal_string()?;
Ok(name)
} else {
let name = buf.parse_cstring()?;
Ok(name)
}
}
}
impl<'t> fmt::Debug for Symbol<'t> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Symbol{{ kind: 0x{:4x} [{} bytes] }}", self.raw_kind(), self.0.len())
}
}
const CVPSF_CODE: u32 = 0x00000001;
const CVPSF_FUNCTION: u32 = 0x00000002;
const CVPSF_MANAGED: u32 = 0x00000004;
const CVPSF_MSIL: u32 = 0x00000008;
fn parse_symbol_data(kind: u16, data: &[u8]) -> Result<SymbolData> {
let mut buf = ParseBuffer::from(data);
match kind {
S_PUB32 | S_PUB32_ST => {
let flags = buf.parse_u32()?;
Ok(SymbolData::PublicSymbol(PublicSymbol {
code: flags & CVPSF_CODE != 0,
function: flags & CVPSF_FUNCTION != 0,
managed: flags & CVPSF_MANAGED != 0,
msil: flags & CVPSF_MSIL != 0,
offset: buf.parse_u32()?,
segment: buf.parse_u16()?,
}))
}
S_LDATA32 | S_LDATA32_ST |
S_GDATA32 | S_GDATA32_ST |
S_LMANDATA | S_LMANDATA_ST |
S_GMANDATA | S_GMANDATA_ST => {
Ok(SymbolData::DataSymbol(DataSymbol {
global: match kind { S_GDATA32 | S_GDATA32_ST | S_GMANDATA | S_GMANDATA_ST => true, _ => false },
managed: match kind { S_LMANDATA | S_LMANDATA_ST | S_GMANDATA | S_GMANDATA_ST => true, _ => false },
type_index: buf.parse_u32()?,
offset: buf.parse_u32()?,
segment: buf.parse_u16()?,
}))
}
S_PROCREF | S_PROCREF_ST |
S_LPROCREF | S_LPROCREF_ST => {
Ok(SymbolData::ProcedureReference(ProcedureReferenceSymbol {
global: match kind { S_PROCREF | S_PROCREF_ST => true, _ => false },
sum_name: buf.parse_u32()?,
symbol_index: buf.parse_u32()?,
module: buf.parse_u16()?,
}))
},
S_DATAREF | S_DATAREF_ST => {
Ok(SymbolData::DataReference(DataReferenceSymbol {
sum_name: buf.parse_u32()?,
symbol_index: buf.parse_u32()?,
module: buf.parse_u16()?,
}))
}
S_ANNOTATIONREF => {
Ok(SymbolData::AnnotationReference(AnnotationReferenceSymbol {
sum_name: buf.parse_u32()?,
symbol_index: buf.parse_u32()?,
module: buf.parse_u16()?,
}))
}
S_CONSTANT | S_CONSTANT_ST => {
Ok(SymbolData::Constant(ConstantSymbol {
type_index: buf.parse_u32()?,
value: buf.parse_variant()?,
}))
}
S_UDT | S_UDT_ST => {
Ok(SymbolData::UserDefinedType(UserDefinedTypeSymbol {
type_index: buf.parse_u32()?,
}))
}
S_LTHREAD32 | S_LTHREAD32_ST |
S_GTHREAD32 | S_GTHREAD32_ST => {
Ok(SymbolData::ThreadStorage(ThreadStorageSymbol {
global: match kind { S_GTHREAD32 | S_GTHREAD32_ST => true, _ => false },
type_index: buf.parse_u32()?,
offset: buf.parse_u32()?,
segment: buf.parse_u16()?,
}))
}
S_LPROC32 | S_LPROC32_ST |
S_GPROC32 | S_GPROC32_ST |
S_LPROC32_ID |
S_GPROC32_ID |
S_LPROC32_DPC |
S_LPROC32_DPC_ID => {
Ok(SymbolData::Procedure(ProcedureSymbol{
global: match kind { S_GPROC32 | S_GPROC32_ST | S_GPROC32_ID => true, _ => false },
parent: buf.parse_u32()?,
end: buf.parse_u32()?,
next: buf.parse_u32()?,
len: buf.parse_u32()?,
dbg_start_offset: buf.parse_u32()?,
dbg_end_offset: buf.parse_u32()?,
type_index: buf.parse_u32()?,
offset: buf.parse_u32()?,
segment: buf.parse_u16()?,
flags: ProcedureFlags::new(buf.parse_u8()?)
}))
}
S_OBJNAME | S_OBJNAME_ST => {
Ok(SymbolData::ObjName(ObjNameSymbol {
signature: buf.parse_u32()?,
}))
}
S_COMPILE3 => {
Ok(SymbolData::Compile3(Compile3Symbol {
language: buf.parse_u8()?.into(),
flags: [buf.parse_u8()?, buf.parse_u8()?, buf.parse_u8()?],
cpu_type: buf.parse_u16()?.into(),
frontend_version: [buf.parse_u16()?, buf.parse_u16()?, buf.parse_u16()?, buf.parse_u16()?],
backend_version: [buf.parse_u16()?, buf.parse_u16()?, buf.parse_u16()?, buf.parse_u16()?],
}))
}
S_UNAMESPACE | S_UNAMESPACE_ST => {
Ok(SymbolData::Namespace(NamespaceSymbol {}))
}
_ => Err(Error::UnimplementedSymbolKind(kind))
}
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub enum SymbolData {
PublicSymbol(PublicSymbol),
DataSymbol(DataSymbol),
ProcedureReference(ProcedureReferenceSymbol),
DataReference(DataReferenceSymbol),
AnnotationReference(AnnotationReferenceSymbol),
Constant(ConstantSymbol),
UserDefinedType(UserDefinedTypeSymbol),
ThreadStorage(ThreadStorageSymbol),
Procedure(ProcedureSymbol),
ObjName(ObjNameSymbol),
Compile3(Compile3Symbol),
Namespace(NamespaceSymbol),
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub struct PublicSymbol {
pub code: bool,
pub function: bool,
pub managed: bool,
pub msil: bool,
pub offset: u32,
pub segment: u16,
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub struct DataSymbol {
pub global: bool,
pub managed: bool,
pub type_index: TypeIndex,
pub offset: u32,
pub segment: u16,
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub struct ProcedureReferenceSymbol {
pub global: bool,
pub sum_name: u32,
pub symbol_index: u32,
pub module: u16,
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub struct DataReferenceSymbol {
pub sum_name: u32,
pub symbol_index: u32,
pub module: u16,
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub struct AnnotationReferenceSymbol {
pub sum_name: u32,
pub symbol_index: u32,
pub module: u16,
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub struct ConstantSymbol {
pub type_index: TypeIndex,
pub value: Variant,
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub struct UserDefinedTypeSymbol {
pub type_index: TypeIndex,
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub struct ThreadStorageSymbol {
pub global: bool,
pub type_index: TypeIndex,
pub offset: u32,
pub segment: u16,
}
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;
#[derive(Debug,Copy,Clone,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 ProcedureFlags {
fn new(flags: u8) -> ProcedureFlags {
ProcedureFlags{
nofpo: flags & CV_PFLAG_NOFPO != 0,
int: flags & CV_PFLAG_INT != 0,
far: flags & CV_PFLAG_FAR != 0,
never: flags & CV_PFLAG_NEVER != 0,
notreached: flags & CV_PFLAG_NOTREACHED != 0,
cust_call: flags & CV_PFLAG_CUST_CALL != 0,
noinline: flags & CV_PFLAG_NOINLINE != 0,
optdbginfo: flags & CV_PFLAG_OPTDBGINFO != 0
}
}
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub struct ProcedureSymbol {
pub global: bool,
pub parent: u32,
pub end: u32,
pub next: u32,
pub len: u32,
pub dbg_start_offset: u32,
pub dbg_end_offset: u32,
pub type_index: TypeIndex,
pub offset: u32,
pub segment: u16,
pub flags: ProcedureFlags
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub struct ObjNameSymbol {
pub signature: u32,
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub struct Compile3Symbol {
pub language: SourceLanguage,
pub flags: [u8; 3],
pub cpu_type: CPUType,
pub frontend_version: [u16; 4],
pub backend_version: [u16; 4],
}
#[derive(Debug,Copy,Clone,Eq,PartialEq)]
pub struct NamespaceSymbol {
}
#[derive(Debug)]
pub struct SymbolIter<'t> {
buf: ParseBuffer<'t>,
}
impl<'t> SymbolIter<'t> {
pub fn new(buf: ParseBuffer<'t>) -> SymbolIter {
SymbolIter { buf }
}
}
impl<'t> FallibleIterator for SymbolIter<'t> {
type Item = Symbol<'t>;
type Error = Error;
fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> {
if self.buf.len() == 0 {
return Ok(None);
}
let symbol_length = self.buf.parse_u16()? as usize;
if symbol_length < 2 {
return Err(Error::SymbolTooShort);
}
let symbol = self.buf.take(symbol_length)?;
Ok(Some(Symbol(symbol)))
}
}
#[cfg(test)]
mod tests {
mod parsing {
use common::*;
use symbol::*;
fn parse<'s>(buf: &'s [u8]) -> Result<(Symbol<'s>,SymbolData,String)> {
let symbol = Symbol(buf);
let data = symbol.parse()?;
let name = symbol.name()?.to_string().into_owned();
Ok((symbol, data, name))
}
#[test]
fn kind_110e() {
let buf = &[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, data, name) = parse(buf).expect("parse");
assert_eq!(symbol.raw_kind(), 0x110e);
assert_eq!(data, SymbolData::PublicSymbol(PublicSymbol { code: false, function: true, managed: false, msil: false, offset: 21952, segment: 1 }));
assert_eq!(name, "__local_stdio_printf_options");
}
#[test]
fn kind_1125() {
let buf = &[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, data, name) = parse(buf).expect("parse");
assert_eq!(symbol.raw_kind(), 0x1125);
assert_eq!(data, SymbolData::ProcedureReference(ProcedureReferenceSymbol { global: true, sum_name: 0, symbol_index: 108, module: 1 }));
assert_eq!(name, "Baz::f_public");
}
#[test]
fn kind_1108() {
let buf = &[8, 17, 112, 6, 0, 0, 118, 97, 95, 108, 105, 115, 116, 0];
let (symbol, data, name) = parse(buf).expect("parse");
assert_eq!(symbol.raw_kind(), 0x1108);
assert_eq!(data, SymbolData::UserDefinedType(UserDefinedTypeSymbol { type_index: 1648 }));
assert_eq!(name, "va_list");
}
#[test]
fn kind_1107() {
let buf = &[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, data, name) = parse(buf).expect("parse");
assert_eq!(symbol.raw_kind(), 0x1107);
assert_eq!(data, SymbolData::Constant(ConstantSymbol { type_index: 4809, value: Variant::U16(1) }));
assert_eq!(name, "__ISA_AVAILABLE_SSE2");
}
#[test]
fn kind_110d() {
let buf = &[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, data, name) = parse(buf).expect("parse");
assert_eq!(symbol.raw_kind(), 0x110d);
assert_eq!(data, SymbolData::DataSymbol(DataSymbol { global: true, managed: false, type_index: 116, offset: 16, segment: 3 }));
assert_eq!(name, "__isa_available");
}
#[test]
fn kind_110c() {
let buf = &[12, 17, 32, 0, 0, 0, 240, 36, 1, 0, 2, 0, 36, 120, 100, 97, 116, 97, 115, 121, 109, 0];
let (symbol, data, name) = parse(buf).expect("parse");
assert_eq!(symbol.raw_kind(), 0x110c);
assert_eq!(data, SymbolData::DataSymbol(DataSymbol { global: false, managed: false, type_index: 32, offset: 74992, segment: 2 }));
assert_eq!(name, "$xdatasym");
}
#[test]
fn kind_1127() {
let buf = &[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, data, name) = parse(buf).expect("parse");
assert_eq!(symbol.raw_kind(), 0x1127);
assert_eq!(data, SymbolData::ProcedureReference(ProcedureReferenceSymbol { global: false, sum_name: 0, symbol_index: 1152, module: 182 }));
assert_eq!(name, "capture_current_context");
}
#[test]
fn kind_1110() {
let buf = &[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, data, name) = parse(buf).expect("parse");
assert_eq!(symbol.raw_kind(), 0x1110);
assert_eq!(data, SymbolData::Procedure(ProcedureSymbol { global: true, parent: 0, end: 560, next: 0, len: 6, dbg_start_offset: 5, dbg_end_offset: 5, type_index: 4103, offset: 21824, segment: 1, flags: ProcedureFlags { nofpo: false, int: false, far: false, never: false, notreached: false, cust_call: false, noinline: false, optdbginfo: false } }));
assert_eq!(name, "Baz::f_protected");
}
#[test]
fn kind_110f() {
let buf = &[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, data, name) = parse(buf).expect("parse");
assert_eq!(symbol.raw_kind(), 0x110f);
assert_eq!(data, SymbolData::Procedure(ProcedureSymbol { global: false, parent: 0, end: 412, next: 0, len: 18, dbg_start_offset: 4, dbg_end_offset: 9, type_index: 4224, offset: 22468, segment: 1, flags: ProcedureFlags { nofpo: false, int: false, far: false, never: false, notreached: false, cust_call: false, noinline: false, optdbginfo: true } }));
assert_eq!(name, "__scrt_common_main");
}
}
}