Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jingle/examples/stack_offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use jingle::analysis::pcode_store::{self, PcodeOpRef};
use jingle::analysis::valuation::{
MergeBehavior, SimpleValuation, SimpleValuationAnalysis, SimpleValuationState,
};
use jingle::display::JingleDisplayable;
use jingle::display::JingleDisplay;
use jingle::modeling::machine::cpu::concrete::ConcretePcodeAddress;
use jingle_sleigh::VarNode;
use jingle_sleigh::context::image::gimli::load_with_gimli;
Expand Down
2 changes: 1 addition & 1 deletion jingle/examples/stack_offset_smt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use jingle::analysis::location::{BasicLocationAnalysis, CallBehavior};
use jingle::analysis::pcode_store::PcodeStore;
use jingle::analysis::pcode_store::{self, PcodeOpRef};
use jingle::analysis::valuation::{MergeBehavior, SmtVal, SmtValuationAnalysis, SmtValuationState};
use jingle::display::JingleDisplayable;
use jingle::display::JingleDisplay;
use jingle::modeling::machine::cpu::concrete::ConcretePcodeAddress;
use jingle_sleigh::VarNode;
use jingle_sleigh::context::image::gimli::load_with_gimli;
Expand Down
4 changes: 2 additions & 2 deletions jingle/src/analysis/cpa/lattice/pcode.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::analysis::cpa::lattice::JoinSemiLattice;
use crate::analysis::cpa::state::{AbstractState, LocationState, MergeOutcome, Successor};
use crate::display::JingleDisplayable;
use crate::display::JingleDisplay;
use crate::modeling::machine::cpu::concrete::ConcretePcodeAddress;
use jingle_sleigh::{IndirectVarNode, PcodeOperation};
use std::borrow::Borrow;
Expand All @@ -22,7 +22,7 @@ pub enum PcodeAddressLattice {
Top,
}

impl JingleDisplayable for PcodeAddressLattice {
impl JingleDisplay for PcodeAddressLattice {
fn fmt_jingle(
&self,
f: &mut Formatter<'_>,
Expand Down
71 changes: 2 additions & 69 deletions jingle/src/analysis/pcode_store.rs
Original file line number Diff line number Diff line change
@@ -1,75 +1,8 @@
use crate::modeling::machine::cpu::concrete::ConcretePcodeAddress;
use jingle_sleigh::PcodeOperation;
use jingle_sleigh::context::loaded::LoadedSleighContext;
use std::{
borrow::{Borrow, Cow},
fmt::Debug,
};
use std::borrow::Borrow;

/// PcodeOpRef — a small, ergonomic wrapper for p-code operations
///
/// `PcodeOpRef` encapsulates either a borrowed reference to a `PcodeOperation`
/// or an owned `PcodeOperation` (internally using `Cow`). This hides the `Cow`
/// type from the rest of the codebase and provides simple helper methods so
/// callers don't need to care about ownership when they only need a `&PcodeOperation`.
///
/// Why this exists
/// - Some stores (e.g., an in-memory CFG) can return a reference to an operation
/// stored inside the structure (no clone required).
/// - Other stores (e.g., `LoadedSleighContext::instruction_at`) construct an
/// `Instruction` on each call and therefore must return an owned `PcodeOperation`.
/// - `PcodeOpRef` lets the store return either without exposing `Cow` to callers.
///
/// Basic usage
/// ```ignore
/// // Get an op from a pcode store (may be borrowed or owned internally)
/// if let Some(op_ref) = store.get_pcode_op_at(addr) {
/// // Use the borrowed reference for transfer/inspection:
/// let op: &PcodeOperation = op_ref.as_ref();
/// // When you need an owned op, clone the reference:
/// let owned_op: PcodeOperation = op_ref.as_ref().clone();
/// }
/// ```
///
/// Note: there is no
/// `into_owned` method on `PcodeOpRef` in order to keep the
/// abstraction minimal; callers that need an owned value can call `.as_ref().clone()`.
#[derive(Clone)]
pub struct PcodeOpRef<'a>(std::borrow::Cow<'a, PcodeOperation>);

impl<'a> AsRef<PcodeOperation> for PcodeOpRef<'a> {
fn as_ref(&self) -> &PcodeOperation {
self.0.as_ref()
}
}

impl<'a> std::ops::Deref for PcodeOpRef<'a> {
type Target = PcodeOperation;
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<'a> From<PcodeOperation> for PcodeOpRef<'a> {
fn from(op: PcodeOperation) -> Self {
PcodeOpRef(Cow::Owned(op))
}
}

impl<'a> From<&'a PcodeOperation> for PcodeOpRef<'a> {
fn from(op: &'a PcodeOperation) -> Self {
PcodeOpRef(Cow::Borrowed(op))
}
}

impl<'a> Debug for PcodeOpRef<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.0 {
Cow::Borrowed(o) => o.fmt(f),
Cow::Owned(o) => o.fmt(f),
}
}
}
pub use jingle_sleigh::PcodeOpRef;

/// A store of p-code operations.
///
Expand Down
4 changes: 2 additions & 2 deletions jingle/src/analysis/valuation/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::analysis::cpa::residue::EmptyResidue;
use crate::analysis::cpa::state::{AbstractState, MergeOutcome, Successor};
use crate::analysis::cpa::{ConfigurableProgramAnalysis, IntoState};
use crate::analysis::varnode_map::VarNodeMap;
use crate::display::JingleDisplayable;
use crate::display::JingleDisplay;
use crate::modeling::machine::cpu::concrete::ConcretePcodeAddress;
use jingle_sleigh::{GeneralizedVarNode, PcodeOperation, SleighArchInfo, SpaceType, VarNode};
use std::borrow::Borrow;
Expand Down Expand Up @@ -333,7 +333,7 @@ impl SimpleValuation {
}
}

impl JingleDisplayable for SimpleValuation {
impl JingleDisplay for SimpleValuation {
fn fmt_jingle(&self, f: &mut Formatter<'_>, info: &SleighArchInfo) -> std::fmt::Result {
match self {
SimpleValuation::Entry(vn) => write!(f, "Entry({})", vn.display(info)),
Expand Down
4 changes: 2 additions & 2 deletions jingle/src/analysis/valuation/smt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::analysis::cpa::residue::EmptyResidue;
use crate::analysis::cpa::state::{AbstractState, MergeOutcome, Successor};
use crate::analysis::cpa::{ConfigurableProgramAnalysis, IntoState};
use crate::analysis::varnode_map::VarNodeMap;
use crate::display::JingleDisplayable;
use crate::display::JingleDisplay;
use crate::modeling::machine::cpu::concrete::ConcretePcodeAddress;
use jingle_sleigh::{GeneralizedVarNode, PcodeOperation, SleighArchInfo, SpaceType, VarNode};
use std::borrow::Borrow;
Expand Down Expand Up @@ -64,7 +64,7 @@ impl Hash for SmtVal {
}
}

impl JingleDisplayable for SmtVal {
impl JingleDisplay for SmtVal {
fn fmt_jingle(&self, f: &mut Formatter<'_>, info: &SleighArchInfo) -> std::fmt::Result {
match self {
SmtVal::Top => write!(f, "⊤"),
Expand Down
201 changes: 5 additions & 196 deletions jingle/src/display.rs
Original file line number Diff line number Diff line change
@@ -1,99 +1,10 @@
use crate::varnode::{ResolvedIndirectVarNode, ResolvedVarnode};
use jingle_sleigh::{
GeneralizedVarNode, IndirectVarNode, Instruction, PcodeOperation, SleighArchInfo, SpaceType,
VarNode,
};
use std::fmt::{Display, Formatter};
use jingle_sleigh::SleighArchInfo;
pub use jingle_sleigh::{JingleDisplay, JingleDisplayWrapper};
use std::fmt::Formatter;
use z3::ast::Ast;

pub trait JingleDisplayable: Sized + Clone {
fn fmt_jingle(&self, f: &mut Formatter<'_>, info: &SleighArchInfo) -> std::fmt::Result;

fn display(&self, info: &SleighArchInfo) -> JingleDisplay<Self> {
JingleDisplay {
info: info.clone(),
inner: self.clone(),
}
}
}

#[derive(Clone)]
pub struct JingleDisplay<T> {
info: SleighArchInfo,
inner: T,
}

impl<T> JingleDisplay<T> {
pub fn inner(&self) -> &T {
&self.inner
}

pub fn info(&self) -> &SleighArchInfo {
&self.info
}
}

impl<T: JingleDisplayable> Display for JingleDisplay<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.inner.fmt_jingle(f, &self.info)
}
}

impl JingleDisplay<ResolvedIndirectVarNode> {
pub fn space_name(&self) -> &str {
self.info
.get_space(self.inner.pointer_space_idx)
.unwrap()
.name
.as_str()
}
}

impl JingleDisplayable for VarNode {
fn fmt_jingle(&self, f: &mut Formatter<'_>, ctx: &SleighArchInfo) -> std::fmt::Result {
if self.space_index == VarNode::CONST_SPACE_INDEX {
write!(f, "{:#x}:{}", self.offset, self.size)
} else if let Some(name) = ctx.register_name(self) {
write!(f, "{name}")
} else if let Some(SpaceType::IPTR_INTERNAL) =
ctx.get_space(self.space_index).map(|s| s._type)
{
write!(f, "$U{:x}:{}", self.offset, self.size)
} else {
write!(
f,
"[{}]{:#x}:{}",
ctx.get_space(self.space_index).ok_or(std::fmt::Error)?.name,
self.offset,
self.size
)
}
}
}

impl JingleDisplayable for IndirectVarNode {
fn fmt_jingle(&self, f: &mut Formatter<'_>, ctx: &SleighArchInfo) -> std::fmt::Result {
write!(
f,
"{}({})",
ctx.get_space(self.pointer_space_index)
.ok_or(std::fmt::Error)?
.name,
self.pointer_location.display(ctx)
)
}
}

impl JingleDisplayable for GeneralizedVarNode {
fn fmt_jingle(&self, f: &mut Formatter<'_>, ctx: &SleighArchInfo) -> std::fmt::Result {
match self {
GeneralizedVarNode::Direct(d) => d.fmt_jingle(f, ctx),
GeneralizedVarNode::Indirect(i) => i.fmt_jingle(f, ctx),
}
}
}

impl JingleDisplayable for ResolvedIndirectVarNode {
impl JingleDisplay for ResolvedIndirectVarNode {
fn fmt_jingle(&self, f: &mut Formatter<'_>, ctx: &SleighArchInfo) -> std::fmt::Result {
write!(
f,
Expand All @@ -106,113 +17,11 @@ impl JingleDisplayable for ResolvedIndirectVarNode {
}
}

impl JingleDisplayable for ResolvedVarnode {
impl JingleDisplay for ResolvedVarnode {
fn fmt_jingle(&self, f: &mut Formatter<'_>, ctx: &SleighArchInfo) -> std::fmt::Result {
match self {
ResolvedVarnode::Direct(a) => a.fmt_jingle(f, ctx),
ResolvedVarnode::Indirect(i) => i.fmt_jingle(f, ctx),
}
}
}

impl JingleDisplayable for PcodeOperation {
fn fmt_jingle(&self, f: &mut Formatter<'_>, ctx: &SleighArchInfo) -> std::fmt::Result {
if let Some(o) = self.output() {
write!(f, "{} = ", o.display(ctx))?;
}
write!(f, "{} ", self.opcode())?;
let mut args: Vec<String> = vec![];
for x in self.inputs() {
args.push(format!("{}", x.display(ctx)));
}
write!(f, "{}", args.join(", "))?;
Ok(())
}
}

impl JingleDisplayable for Instruction {
fn fmt_jingle(&self, f: &mut Formatter<'_>, ctx: &SleighArchInfo) -> std::fmt::Result {
writeln!(f, "{} {}", self.disassembly.mnemonic, self.disassembly.args)?;
for x in &self.ops {
writeln!(f, "\t{}", x.display(ctx))?;
}
Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
use jingle_sleigh::context::SleighContextBuilder;
use jingle_sleigh::{PcodeOperation, VarNode};

fn make_sleigh() -> jingle_sleigh::context::SleighContext {
// Mirrors other tests in the repo which expect a local Ghidra checkout at this path.
let ctx_builder =
SleighContextBuilder::load_ghidra_installation("/Applications/ghidra").unwrap();
ctx_builder.build("x86:LE:64:default").unwrap()
}

#[test]
fn test_varnode_display_cases() {
let sleigh = make_sleigh();
let info = sleigh.arch_info().clone();

// const varnode should display as "<hex_offset>:<size>"
let const_vn = VarNode {
space_index: VarNode::CONST_SPACE_INDEX,
offset: 0x42,
size: 1,
};
assert_eq!(format!("{}", const_vn.display(&info)), "0x42:1");

// registers: if registers exist, pretty display should equal the register name
let regs: Vec<_> = sleigh.arch_info().registers().collect();
if !regs.is_empty() {
let (vn, name) = regs[0].clone();
assert_eq!(format!("{}", vn.display(&info)), name);
}

// other space: we expect a bracketed form like "[<space>]offset:size" or internal $U...
let other = VarNode {
space_index: 1,
offset: 0x10,
size: 4,
};
let s = format!("{}", other.display(&info));
// be permissive: ensure it contains the size and either bracket or unique prefix
assert!(s.contains(":4"));
}

#[test]
fn test_pcode_display_and_round_trip_copy() {
let sleigh = make_sleigh();
let info = sleigh.arch_info().clone();

let output = VarNode {
space_index: 4,
offset: 0x20,
size: 4,
};
let input = VarNode {
space_index: VarNode::CONST_SPACE_INDEX,
offset: 0x1,
size: 4,
};

let op = PcodeOperation::Copy {
input: input.clone(),
output: output.clone(),
};

// display formatting contains the opcode name and operands
let s = format!("{}", op.display(&info));
assert_eq!(s, "ESP = COPY 0x1:4");
// Try to round-trip: render to textual pcode and parse with SleighContext.parse_pcode_listing
// Use the same textual format that `display` produces for varnodes.

let parsed = sleigh.parse_pcode_listing(s).unwrap();
assert_eq!(parsed.len(), 1);
assert_eq!(parsed[0], op);
}
}
2 changes: 1 addition & 1 deletion jingle/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anyhow::Context;
use clap::{Parser, Subcommand};
use hex::decode;
use jingle::display::JingleDisplayable;
use jingle::display::JingleDisplay;
use jingle::modeling::{ModeledBlock, ModelingContext, State};
use jingle_sleigh::context::SleighContextBuilder;
use jingle_sleigh::context::loaded::LoadedSleighContext;
Expand Down
2 changes: 1 addition & 1 deletion jingle/src/python/instruction.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::display::JingleDisplayable;
use crate::sleigh::context::loaded::LoadedSleighContext;
use crate::sleigh::{Instruction, PcodeOperation};
use jingle_sleigh::SleighArchInfo;
use jingle_sleigh::display::JingleDisplay;
use pyo3::{pyclass, pymethods};
use std::borrow::Borrow;
use std::fmt::{Display, Formatter};
Expand Down
2 changes: 1 addition & 1 deletion jingle/src/python/modeled_block.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::display::JingleDisplayable;
use crate::modeling::{ModeledBlock, ModelingContext};
use crate::python::instruction::PythonInstruction;
use crate::python::resolved_varnode::PythonResolvedVarNode;
use crate::python::state::PythonState;
use crate::python::varode_iterator::VarNodeIterator;
use crate::sleigh::Instruction;
use jingle_sleigh::SleighArchInfo;
use jingle_sleigh::display::JingleDisplay;
use pyo3::{PyResult, pyclass, pymethods};
use std::borrow::Borrow;

Expand Down
Loading