use std::collections::BTreeMap;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum RawExpressionResult {
DirectValue(DirectValueResult),
MemoryLocation(LocationResult),
Optimized,
#[allow(dead_code)]
Composite(Vec<PieceResult>),
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum DirectValueResult {
Constant(i64),
AbsoluteAddress(u64),
ImplicitValue(Vec<u8>),
RegisterValue(u16),
ComputedValue {
steps: Vec<PlanExprOp>,
result_size: MemoryAccessSize,
},
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum LocationResult {
Address(u64),
RegisterAddress {
register: u16, offset: Option<i64>,
size: Option<u64>, },
ComputedLocation {
steps: Vec<PlanExprOp>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub enum CfaResult {
RegisterPlusOffset {
register: u16, offset: i64,
},
Expression { steps: Vec<PlanExprOp> },
}
#[derive(Debug, Clone, PartialEq)]
pub struct CallerFrameRecovery {
pub cfa_steps: Vec<PlanExprOp>,
pub return_address_register: u16,
pub caller_pc_steps: Vec<PlanExprOp>,
pub register_recovery_steps: BTreeMap<u16, Vec<PlanExprOp>>,
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct PieceResult {
pub(crate) location: RawExpressionResult,
pub(crate) size: u64,
pub(crate) bit_offset: Option<u64>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct EntryValueCase {
pub caller_return_pc: u64,
pub value_steps: Vec<PlanExprOp>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PlanExprOp {
LoadRegister(u16),
PushConstant(i64),
Dereference {
size: MemoryAccessSize,
},
FormTlsAddress,
Add,
Sub,
Mul,
Div,
Mod,
And,
Or,
Xor,
Shl,
Shr,
Shra,
Not,
Neg,
Abs,
Dup,
Drop,
Swap,
Rot,
Pick(u8),
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
If {
then_branch: Vec<PlanExprOp>,
else_branch: Vec<PlanExprOp>,
},
EntryValueLookup {
caller_pc_steps: Vec<PlanExprOp>,
cases: Vec<EntryValueCase>,
},
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MemoryAccessSize {
U8, U16, U32, U64, }
impl MemoryAccessSize {
pub fn bytes(&self) -> usize {
match self {
MemoryAccessSize::U8 => 1,
MemoryAccessSize::U16 => 2,
MemoryAccessSize::U32 => 4,
MemoryAccessSize::U64 => 8,
}
}
pub fn from_size(size: u64) -> Self {
match size {
1 => MemoryAccessSize::U8,
2 => MemoryAccessSize::U16,
4 => MemoryAccessSize::U32,
8 => MemoryAccessSize::U64,
_ if size <= 8 => MemoryAccessSize::U64, _ => MemoryAccessSize::U64, }
}
}
impl DirectValueResult {
fn steps_to_expression(steps: &[PlanExprOp]) -> String {
use ghostscope_platform::register_mapping::dwarf_reg_to_name;
let mut stack: Vec<String> = Vec::new();
for step in steps {
match step {
PlanExprOp::LoadRegister(r) => {
let reg_name = dwarf_reg_to_name(*r).unwrap_or("r?").to_string();
stack.push(reg_name);
}
PlanExprOp::PushConstant(v) => {
if *v >= 0 && *v <= 0xFF {
stack.push(format!("{v}"));
} else {
stack.push(format!("0x{v:x}"));
}
}
PlanExprOp::Add => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
if a.chars()
.all(|c| c.is_ascii_uppercase() || c.is_ascii_digit())
&& b.parse::<i64>().is_ok()
&& b.parse::<i64>().unwrap().abs() < 1000
{
stack.push(format!("{a}+{b}"));
} else {
stack.push(format!("({a}+{b})"));
}
} else {
stack.push("?+?".to_string());
}
}
PlanExprOp::Sub => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}-{b})"));
} else {
stack.push("?-?".to_string());
}
}
PlanExprOp::Mul => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("{a}*{b}"));
} else {
stack.push("?*?".to_string());
}
}
PlanExprOp::Div => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}/{b})"));
} else {
stack.push("?/?".to_string());
}
}
PlanExprOp::Mod => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}%{b})"));
} else {
stack.push("?%?".to_string());
}
}
PlanExprOp::And => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}&{b})"));
} else {
stack.push("?&?".to_string());
}
}
PlanExprOp::Or => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}|{b})"));
} else {
stack.push("?|?".to_string());
}
}
PlanExprOp::Xor => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}^{b})"));
} else {
stack.push("?^?".to_string());
}
}
PlanExprOp::Shl => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}<<{b})"));
} else {
stack.push("?<<?".to_string());
}
}
PlanExprOp::Shr => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}>>{b})"));
} else {
stack.push("?>>?".to_string());
}
}
PlanExprOp::Shra => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}>>>{b})"));
} else {
stack.push("?>>>?".to_string());
}
}
PlanExprOp::Not => {
if let Some(a) = stack.pop() {
stack.push(format!("~{a}"));
} else {
stack.push("~?".to_string());
}
}
PlanExprOp::Neg => {
if let Some(a) = stack.pop() {
stack.push(format!("-{a}"));
} else {
stack.push("-?".to_string());
}
}
PlanExprOp::Abs => {
if let Some(a) = stack.pop() {
stack.push(format!("|{a}|"));
} else {
stack.push("|?|".to_string());
}
}
PlanExprOp::Dereference { size } => {
if let Some(a) = stack.pop() {
stack.push(format!("*({a} as {size})"));
} else {
stack.push(format!("*(? as {size})"));
}
}
PlanExprOp::FormTlsAddress => {
if let Some(a) = stack.pop() {
stack.push(format!("tls({a})"));
} else {
stack.push("tls(?)".to_string());
}
}
PlanExprOp::Dup => {
if let Some(top) = stack.last() {
stack.push(top.clone());
}
}
PlanExprOp::Drop => {
stack.pop();
}
PlanExprOp::Swap => {
if stack.len() >= 2 {
let len = stack.len();
stack.swap(len - 1, len - 2);
}
}
PlanExprOp::Rot => {
if stack.len() >= 3 {
let len = stack.len();
let third = stack.remove(len - 3);
stack.push(third);
}
}
PlanExprOp::Pick(n) => {
if stack.len() > *n as usize {
let idx = stack.len() - 1 - (*n as usize);
let val = stack[idx].clone();
stack.push(val);
} else {
stack.push("?".to_string());
}
}
PlanExprOp::Eq => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}=={b})"));
} else {
stack.push("?==?".to_string());
}
}
PlanExprOp::Ne => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}!={b})"));
} else {
stack.push("?!=?".to_string());
}
}
PlanExprOp::Lt => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}<{b})"));
} else {
stack.push("?<?".to_string());
}
}
PlanExprOp::Le => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}<={b})"));
} else {
stack.push("?<=?".to_string());
}
}
PlanExprOp::Gt => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}>{b})"));
} else {
stack.push("?>?".to_string());
}
}
PlanExprOp::Ge => {
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
stack.push(format!("({a}>={b})"));
} else {
stack.push("?>=?".to_string());
}
}
PlanExprOp::If {
then_branch,
else_branch,
} => {
if let Some(cond) = stack.pop() {
stack.push(format!("if {cond} then ... else ..."));
} else {
stack.push("if ? then ... else ...".to_string());
}
_ = then_branch;
_ = else_branch;
}
PlanExprOp::EntryValueLookup { cases, .. } => {
stack.push(format!("entry_value[{} cases]", cases.len()));
}
}
}
stack.pop().unwrap_or_else(|| "?".to_string())
}
}
pub(crate) fn plan_expr_steps_to_expression(steps: &[PlanExprOp]) -> String {
DirectValueResult::steps_to_expression(steps)
}
impl LocationResult {
fn steps_to_expression(steps: &[PlanExprOp]) -> String {
DirectValueResult::steps_to_expression(steps)
}
}
impl fmt::Display for RawExpressionResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RawExpressionResult::DirectValue(dv) => write!(f, "[DirectValue] {dv}"),
RawExpressionResult::MemoryLocation(loc) => write!(f, "[Memory] {loc}"),
RawExpressionResult::Optimized => write!(f, "<optimized out>"),
RawExpressionResult::Composite(pieces) => {
write!(f, "Composite[{} pieces]", pieces.len())
}
}
}
}
impl fmt::Display for DirectValueResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ghostscope_platform::register_mapping::dwarf_reg_to_name;
match self {
DirectValueResult::Constant(c) => {
if *c >= 0 && *c <= 0xFF {
write!(f, "{c} (0x{c:x})")
} else {
write!(f, "0x{c:x}")
}
}
DirectValueResult::AbsoluteAddress(addr) => write!(f, "&@0x{addr:x}"),
DirectValueResult::RegisterValue(r) => {
if let Some(name) = dwarf_reg_to_name(*r) {
write!(f, "{name}")
} else {
write!(f, "r{r}")
}
}
DirectValueResult::ImplicitValue(bytes) => {
if bytes.len() <= 8 {
write!(f, "implicit[")?;
for (i, b) in bytes.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
write!(f, "{b:02x}")?;
}
write!(f, "]")
} else {
write!(f, "implicit[{} bytes]", bytes.len())
}
}
DirectValueResult::ComputedValue {
steps,
result_size: _,
} => {
write!(f, "=")?;
let expr = Self::steps_to_expression(steps);
write!(f, "{expr}")
}
}
}
}
impl fmt::Display for LocationResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ghostscope_platform::register_mapping::dwarf_reg_to_name;
match self {
LocationResult::Address(addr) => write!(f, "@0x{addr:x}"),
LocationResult::RegisterAddress {
register,
offset,
size,
} => {
let reg_name = dwarf_reg_to_name(*register).unwrap_or("r?");
match (offset, size) {
(Some(o), Some(s)) => {
let offset = *o;
if offset >= 0 {
write!(f, "@[{reg_name}+{offset}]:{s}")
} else {
let neg = -offset;
write!(f, "@[{reg_name}-{neg}]:{s}")
}
}
(Some(o), None) => {
let offset = *o;
if offset >= 0 {
write!(f, "@[{reg_name}+{offset}]")
} else {
let neg = -offset;
write!(f, "@[{reg_name}-{neg}]")
}
}
(None, Some(s)) => write!(f, "@[{reg_name}]:{s}"),
(None, None) => write!(f, "@[{reg_name}]"),
}
}
LocationResult::ComputedLocation { steps } => {
write!(f, "@[")?;
let expr = Self::steps_to_expression(steps);
write!(f, "{expr}]")
}
}
}
}
impl fmt::Display for MemoryAccessSize {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MemoryAccessSize::U8 => write!(f, "u8"),
MemoryAccessSize::U16 => write!(f, "u16"),
MemoryAccessSize::U32 => write!(f, "u32"),
MemoryAccessSize::U64 => write!(f, "u64"),
}
}
}
impl fmt::Display for PlanExprOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ghostscope_platform::register_mapping::dwarf_reg_to_name;
match self {
PlanExprOp::LoadRegister(r) => {
if let Some(name) = dwarf_reg_to_name(*r) {
write!(f, "load {name}")
} else {
write!(f, "load r{r}")
}
}
PlanExprOp::PushConstant(v) => write!(f, "push {v}"),
PlanExprOp::Dereference { size } => write!(f, "deref {size}"),
PlanExprOp::FormTlsAddress => write!(f, "form_tls_address"),
PlanExprOp::Add => write!(f, "add"),
PlanExprOp::Sub => write!(f, "sub"),
PlanExprOp::Mul => write!(f, "mul"),
PlanExprOp::Div => write!(f, "div"),
PlanExprOp::Mod => write!(f, "mod"),
PlanExprOp::And => write!(f, "and"),
PlanExprOp::Or => write!(f, "or"),
PlanExprOp::Xor => write!(f, "xor"),
PlanExprOp::Shl => write!(f, "shl"),
PlanExprOp::Shr => write!(f, "shr"),
PlanExprOp::Shra => write!(f, "shra"),
PlanExprOp::Not => write!(f, "not"),
PlanExprOp::Neg => write!(f, "neg"),
PlanExprOp::Abs => write!(f, "abs"),
PlanExprOp::Dup => write!(f, "dup"),
PlanExprOp::Drop => write!(f, "drop"),
PlanExprOp::Swap => write!(f, "swap"),
PlanExprOp::Rot => write!(f, "rot"),
PlanExprOp::Pick(n) => write!(f, "pick {n}"),
PlanExprOp::Eq => write!(f, "eq"),
PlanExprOp::Ne => write!(f, "ne"),
PlanExprOp::Lt => write!(f, "lt"),
PlanExprOp::Le => write!(f, "le"),
PlanExprOp::Gt => write!(f, "gt"),
PlanExprOp::Ge => write!(f, "ge"),
PlanExprOp::If {
then_branch,
else_branch,
} => {
write!(
f,
"if[then:{} else:{}]",
then_branch.len(),
else_branch.len()
)
}
PlanExprOp::EntryValueLookup { cases, .. } => {
write!(f, "entry_value_lookup[cases:{}]", cases.len())
}
}
}
}