use std::cmp::Ordering;
use std::fmt;
use crate::common::*;
use crate::msf::Stream;
use crate::FallibleIterator;
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum FrameType {
Unknown = 0xff,
FPO = 0,
Trap = 1,
TSS = 2,
Standard = 3,
FrameData = 4,
}
impl fmt::Display for FrameType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Unknown => write!(f, "unknown"),
Self::FPO => write!(f, "fpo"),
Self::Trap => write!(f, "trap"),
Self::TSS => write!(f, "tss"),
Self::Standard => write!(f, "std"),
Self::FrameData => write!(f, "fdata"),
}
}
}
#[repr(C)]
struct NewFrameData {
code_start: u32,
code_size: u32,
locals_size: u32,
params_size: u32,
max_stack_size: u32,
frame_func: u32,
prolog_size: u16,
saved_regs_size: u16,
flags: u32,
}
impl NewFrameData {
pub fn code_start(&self) -> PdbInternalRva {
PdbInternalRva(u32::from_le(self.code_start))
}
pub fn code_size(&self) -> u32 {
u32::from_le(self.code_size)
}
pub fn locals_size(&self) -> u32 {
u32::from_le(self.locals_size)
}
pub fn params_size(&self) -> u32 {
u32::from_le(self.params_size)
}
pub fn max_stack_size(&self) -> u32 {
u32::from_le(self.max_stack_size)
}
pub fn frame_func(&self) -> StringRef {
StringRef(u32::from_le(self.frame_func))
}
pub fn prolog_size(&self) -> u16 {
u16::from_le(self.prolog_size)
}
pub fn saved_regs_size(&self) -> u16 {
u16::from_le(self.saved_regs_size)
}
pub fn has_seh(&self) -> bool {
self.flags() & 1 != 0
}
pub fn has_eh(&self) -> bool {
self.flags() & 2 != 0
}
pub fn is_function_start(&self) -> bool {
self.flags() & 4 != 0
}
fn flags(&self) -> u32 {
u32::from_le(self.flags)
}
}
impl fmt::Debug for NewFrameData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NewFrameData")
.field("code_start", &self.code_start())
.field("code_size", &self.code_size())
.field("locals_size", &self.locals_size())
.field("params_size", &self.params_size())
.field("max_stack_size", &self.max_stack_size())
.field("frame_func", &self.frame_func())
.field("prolog_size", &self.prolog_size())
.field("saved_regs_size", &self.saved_regs_size())
.field("has_seh", &self.has_seh())
.field("has_eh", &self.has_eh())
.field("is_function_start", &self.is_function_start())
.finish()
}
}
#[repr(C)]
struct OldFrameData {
code_start: u32,
code_size: u32,
locals_size: u32,
params_size: u16,
attributes: u16,
}
impl OldFrameData {
pub fn code_start(&self) -> PdbInternalRva {
PdbInternalRva(u32::from_le(self.code_start))
}
pub fn code_size(&self) -> u32 {
u32::from_le(self.code_size)
}
pub fn locals_size(&self) -> u32 {
u32::from_le(self.locals_size)
}
pub fn params_size(&self) -> u16 {
u16::from_le(self.params_size)
}
pub fn prolog_size(&self) -> u16 {
self.attributes() & 0xf
}
pub fn saved_regs_size(&self) -> u16 {
(self.attributes() >> 8) & 0x7
}
pub fn has_seh(&self) -> bool {
self.attributes() & 0x200 != 0
}
pub fn uses_base_pointer(&self) -> bool {
self.attributes() & 0x400 != 0
}
pub fn frame_type(&self) -> FrameType {
match self.attributes() >> 14 {
0x00 => FrameType::FPO,
0x01 => FrameType::Trap,
0x02 => FrameType::TSS,
0x03 => FrameType::Standard,
0x04 => FrameType::FrameData,
_ => FrameType::Unknown,
}
}
fn attributes(&self) -> u16 {
u16::from_le(self.attributes)
}
}
impl fmt::Debug for OldFrameData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OldFrameData")
.field("code_start", &self.code_start())
.field("code_size", &self.code_size())
.field("locals_size", &self.locals_size())
.field("params_size", &self.params_size())
.field("prolog_size", &self.prolog_size())
.field("saved_regs_size", &self.saved_regs_size())
.field("has_seh", &self.has_seh())
.field("uses_base_pointer", &self.uses_base_pointer())
.field("frame_type", &self.frame_type())
.finish()
}
}
#[derive(Clone, Debug)]
pub struct FrameData {
pub ty: FrameType,
pub code_start: PdbInternalRva,
pub code_size: u32,
pub locals_size: u32,
pub params_size: u32,
pub prolog_size: u16,
pub saved_regs_size: u16,
pub max_stack_size: Option<u32>,
pub has_structured_eh: bool,
pub has_cpp_eh: bool,
pub is_function_start: bool,
pub uses_base_pointer: bool,
pub program: Option<StringRef>,
}
impl From<&'_ OldFrameData> for FrameData {
fn from(data: &OldFrameData) -> Self {
Self {
ty: data.frame_type(),
code_start: data.code_start(),
code_size: data.code_size(),
prolog_size: data.prolog_size(),
locals_size: data.locals_size() * 4,
params_size: u32::from(data.params_size()) * 4,
saved_regs_size: data.saved_regs_size() * 4,
max_stack_size: None,
has_structured_eh: data.has_seh(),
has_cpp_eh: false,
is_function_start: false,
uses_base_pointer: data.uses_base_pointer(),
program: None,
}
}
}
impl From<&'_ NewFrameData> for FrameData {
fn from(data: &NewFrameData) -> Self {
Self {
ty: FrameType::FrameData,
code_start: data.code_start(),
code_size: data.code_size(),
prolog_size: data.prolog_size(),
locals_size: data.locals_size(),
params_size: data.params_size(),
saved_regs_size: data.saved_regs_size(),
max_stack_size: Some(data.max_stack_size()),
has_structured_eh: data.has_seh(),
has_cpp_eh: data.has_eh(),
is_function_start: data.is_function_start(),
uses_base_pointer: false,
program: Some(data.frame_func()),
}
}
}
#[derive(Debug, Default)]
pub struct FrameDataIter<'t> {
old_frames: &'t [OldFrameData],
new_frames: &'t [NewFrameData],
old_index: usize,
new_index: usize,
}
impl FallibleIterator for FrameDataIter<'_> {
type Item = FrameData;
type Error = Error;
fn next(&mut self) -> Result<Option<Self::Item>> {
let old_opt = self.old_frames.get(self.old_index);
let new_opt = self.new_frames.get(self.new_index);
Ok(Some(match (old_opt, new_opt) {
(Some(old_frame), Some(new_frame)) => {
match new_frame.code_start().cmp(&old_frame.code_start()) {
Ordering::Less => {
self.new_index += 1;
new_frame.into()
}
Ordering::Equal => {
self.new_index += 1;
self.old_index += 1;
new_frame.into()
}
Ordering::Greater => {
self.old_index += 1;
old_frame.into()
}
}
}
(Some(old_frame), None) => {
self.old_index += 1;
old_frame.into()
}
(None, Some(new_frame)) => {
self.new_index += 1;
new_frame.into()
}
(None, None) => return Ok(None),
}))
}
}
trait AddrRange {
fn start(&self) -> PdbInternalRva;
fn size(&self) -> u32;
#[inline]
fn end(&self) -> PdbInternalRva {
self.start() + self.size()
}
#[inline]
fn contains(&self, rva: PdbInternalRva) -> bool {
rva >= self.start() && rva < self.end()
}
}
impl AddrRange for OldFrameData {
fn start(&self) -> PdbInternalRva {
self.code_start()
}
fn size(&self) -> u32 {
self.code_size()
}
}
impl AddrRange for NewFrameData {
fn start(&self) -> PdbInternalRva {
self.code_start()
}
fn size(&self) -> u32 {
self.code_size()
}
}
fn binary_search_by_rva<R: AddrRange>(frames: &[R], rva: PdbInternalRva) -> usize {
match frames.binary_search_by_key(&rva, |f| f.start()) {
Ok(index) => index,
Err(index) => {
if index > 0 && frames[index - 1].contains(rva) {
index - 1
} else {
index
}
}
}
}
pub struct FrameTable<'s> {
old_stream: Option<Stream<'s>>,
new_stream: Option<Stream<'s>>,
}
impl<'s> FrameTable<'s> {
pub(crate) fn parse(
old_stream: Option<Stream<'s>>,
new_stream: Option<Stream<'s>>,
) -> Result<Self> {
if let Some(ref stream) = old_stream {
if cast_aligned::<OldFrameData>(stream.as_slice()).is_none() {
return Err(Error::InvalidStreamLength("FrameData"));
}
}
if let Some(ref stream) = new_stream {
if cast_aligned::<NewFrameData>(stream.as_slice()).is_none() {
return Err(Error::InvalidStreamLength("FPO"));
}
}
Ok(FrameTable {
old_stream,
new_stream,
})
}
pub fn iter(&self) -> FrameDataIter<'_> {
FrameDataIter {
old_frames: self.old_frames(),
new_frames: self.new_frames(),
old_index: 0,
new_index: 0,
}
}
pub fn iter_at_rva(&self, rva: PdbInternalRva) -> FrameDataIter<'_> {
let old_frames = self.old_frames();
let old_index = binary_search_by_rva(old_frames, rva);
let new_frames = self.new_frames();
let new_index = binary_search_by_rva(new_frames, rva);
FrameDataIter {
old_frames,
new_frames,
old_index,
new_index,
}
}
pub fn is_empty(&self) -> bool {
self.new_frames().is_empty() && self.old_frames().is_empty()
}
fn old_frames(&self) -> &[OldFrameData] {
match self.old_stream {
Some(ref stream) => cast_aligned(stream.as_slice()).unwrap(),
None => &[],
}
}
fn new_frames(&self) -> &[NewFrameData] {
match self.new_stream {
Some(ref stream) => cast_aligned(stream.as_slice()).unwrap(),
None => &[],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::mem;
#[test]
fn test_new_frame_data() {
assert_eq!(mem::size_of::<NewFrameData>(), 32);
assert_eq!(mem::align_of::<NewFrameData>(), 4);
}
#[test]
fn test_old_frame_data() {
assert_eq!(mem::size_of::<OldFrameData>(), 16);
assert_eq!(mem::align_of::<OldFrameData>(), 4);
}
}