use crate::{
addressmap::AddressMap,
vb::{
functype::{ArgType, FuncTypDesc},
procedure::{ProcDscInfo, pcode_frame},
},
};
#[derive(Debug, Clone)]
pub enum FrameVar {
Local {
frame_offset: u16,
},
Argument {
index: u8,
name: Option<String>,
arg_type: Option<ArgType>,
},
Housekeeping {
name: &'static str,
},
Unknown {
offset: i16,
},
}
pub struct FrameResolver {
frame_size: u16,
param_names: Vec<String>,
arg_types: Vec<ArgType>,
}
impl FrameResolver {
pub fn new(
proc_dsc: &ProcDscInfo<'_>,
func_type: Option<&FuncTypDesc<'_>>,
map: &AddressMap<'_>,
) -> Self {
let (param_names, arg_types) = if let Some(ftd) = func_type {
let names: Vec<String> = ftd
.param_names(map)
.into_iter()
.map(|s| String::from_utf8_lossy(s).into_owned())
.collect();
let types = ftd.arg_types();
(names, types)
} else {
(Vec::new(), Vec::new())
};
Self {
frame_size: proc_dsc.frame_size().unwrap_or(0),
param_names,
arg_types,
}
}
pub fn resolve(&self, offset: i16) -> FrameVar {
if offset >= 0x08 {
let arg_byte = i32::from(offset).saturating_sub(0x08);
let arg_index = arg_byte / 4;
let index = u8::try_from(arg_index).unwrap_or(u8::MAX);
let name = self.param_names.get(index as usize).cloned();
let arg_type = self.arg_types.get(index as usize).copied();
return FrameVar::Argument {
index,
name,
arg_type,
};
}
if offset > 0 && offset < 0x08 {
return FrameVar::Unknown { offset };
}
if offset == 0 {
return FrameVar::Unknown { offset };
}
let abs_offset = i32::from(offset).unsigned_abs();
if abs_offset <= pcode_frame::HOUSEKEEPING_SIZE {
let name = match i32::from(offset) {
pcode_frame::PCODE_IP => "pcode_ip",
pcode_frame::CONST_POOL_VA => "const_pool_va",
pcode_frame::PROC_DSC_INFO => "proc_dsc_info",
pcode_frame::ERROR_HANDLER_IP => "error_handler_ip",
pcode_frame::ERROR_TARGET => "error_target",
pcode_frame::ENGINE_CONTEXT => "engine_context",
pcode_frame::ENGINE_TLS => "engine_tls",
pcode_frame::PROC_FLAGS => "proc_flags",
pcode_frame::OBJECT_PTR => "object_ptr",
pcode_frame::ERROR_STATE => "error_state",
pcode_frame::SAVED_PCODE_IP => "saved_pcode_ip",
pcode_frame::HANDLER_FN => "handler_fn",
_ => "housekeeping",
};
return FrameVar::Housekeeping { name };
}
let local_byte = abs_offset.saturating_sub(pcode_frame::HOUSEKEEPING_SIZE);
let frame_offset = u16::try_from(local_byte).unwrap_or(u16::MAX);
if frame_offset <= self.frame_size {
return FrameVar::Local { frame_offset };
}
FrameVar::Unknown { offset }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::addressmap::SectionEntry;
fn make_test_map(file: &[u8]) -> AddressMap<'_> {
AddressMap::from_parts(
file,
0x00400000,
vec![SectionEntry {
virtual_address: 0x1000,
virtual_size: 0x2000,
raw_data_offset: 0x200,
raw_data_size: 0x2000,
}],
)
}
fn make_proc_dsc(frame_size: u16, arg_size: u16) -> Vec<u8> {
let mut data = vec![0u8; 0x1E]; data[0x04..0x06].copy_from_slice(&arg_size.to_le_bytes());
data[0x06..0x08].copy_from_slice(&frame_size.to_le_bytes());
data[0x08..0x0A].copy_from_slice(&0x0050u16.to_le_bytes()); data[0x0A..0x0C].copy_from_slice(&0x001Eu16.to_le_bytes()); data
}
#[test]
fn test_resolve_argument() {
let file = vec![0u8; 0x3000];
let map = make_test_map(&file);
let pdi_data = make_proc_dsc(0x100, 0x10);
let pdi = ProcDscInfo::parse(&pdi_data).unwrap();
let resolver = FrameResolver::new(&pdi, None, &map);
assert!(
matches!(
resolver.resolve(0x08),
FrameVar::Argument {
index: 0,
name: None,
..
}
),
"expected Argument(0), got {:?}",
resolver.resolve(0x08)
);
assert!(
matches!(resolver.resolve(0x0C), FrameVar::Argument { index: 1, .. }),
"expected Argument(1), got {:?}",
resolver.resolve(0x0C)
);
}
#[test]
fn test_resolve_housekeeping() {
let file = vec![0u8; 0x3000];
let map = make_test_map(&file);
let pdi_data = make_proc_dsc(0x100, 0x10);
let pdi = ProcDscInfo::parse(&pdi_data).unwrap();
let resolver = FrameResolver::new(&pdi, None, &map);
assert!(
matches!(
resolver.resolve(-0x5C),
FrameVar::Housekeeping { name: "pcode_ip" }
),
"expected Housekeeping(pcode_ip), got {:?}",
resolver.resolve(-0x5C)
);
assert!(
matches!(
resolver.resolve(-0x30),
FrameVar::Housekeeping { name: "object_ptr" }
),
"expected Housekeeping(object_ptr), got {:?}",
resolver.resolve(-0x30)
);
}
#[test]
fn test_resolve_local() {
let file = vec![0u8; 0x3000];
let map = make_test_map(&file);
let pdi_data = make_proc_dsc(0x100, 0x10);
let pdi = ProcDscInfo::parse(&pdi_data).unwrap();
let resolver = FrameResolver::new(&pdi, None, &map);
assert!(
matches!(resolver.resolve(-0x8C), FrameVar::Local { frame_offset: 4 }),
"expected Local(4), got {:?}",
resolver.resolve(-0x8C)
);
assert!(
matches!(resolver.resolve(-0x90), FrameVar::Local { frame_offset: 8 }),
"expected Local(8), got {:?}",
resolver.resolve(-0x90)
);
}
}