Skip to main content

jingle/modeling/
mod.rs

1use crate::error::JingleError;
2
3use crate::varnode::ResolvedVarnode::{Direct, Indirect};
4use crate::varnode::{ResolvedIndirectVarNode, ResolvedVarnode};
5use jingle_sleigh::{
6    GeneralizedVarNode, PcodeOperation, SleighArchInfo, SpaceType, create_varnode,
7};
8use std::cmp::{Ordering, min};
9use std::collections::HashSet;
10use std::fmt::Debug;
11use std::hash::{DefaultHasher, Hash, Hasher};
12use std::ops::{Add, Neg};
13use tracing::instrument;
14use z3::ast::{Ast, BV, Bool};
15
16mod block;
17mod branch;
18mod concretize;
19pub mod expression;
20mod instruction;
21pub mod machine;
22mod slice;
23mod state;
24pub mod tactics;
25
26pub use block::ModeledBlock;
27pub use branch::*;
28pub use instruction::ModeledInstruction;
29pub use state::State;
30
31/// `jingle` models straight-line traces of computations. This trait represents all the information
32/// needed to model a given trace.
33/// It enforces that the type has a handle to z3, has a concept of program state, and also
34/// defines several helper functions for building formulae
35/// todo: this should probably be separated out with the extension trait pattern
36pub trait ModelingContext: Debug + Sized {
37    /// Get a handle to the jingle context associated with this modeling context
38    fn get_arch_info(&self) -> &SleighArchInfo;
39
40    /// Get the address this context is associated with (e.g. for an instruction, it is the address,
41    /// for a basic block, it is the address of the first instruction).
42    /// Used for building assertions about branch reachability
43    fn get_address(&self) -> u64;
44
45    /// Get the `State` associated with the precondition of this trace
46    fn get_original_state(&self) -> &State;
47    /// Get the `State` associated with the postcondition of this trace
48    fn get_final_state(&self) -> &State;
49
50    /// Get a vec of the operations associated with this trace
51    /// todo: should this be a slice instead of a vec?
52    /// todo: someday when we support paths this should be a graph and not a vec
53    fn get_ops(&self) -> Vec<&PcodeOperation>;
54    /// Get a hashset of the addresses read by this trace. The values returned in this hashset are
55    /// fully modeled: a read from a given varnode will evaluate to its value at the stage in the
56    /// computation that the read was performed. Because of this, these should always be read
57    /// from the `State` returned by `get_final_state`, as it is guaranteed to have a handle to
58    /// all intermediate spaces that may be referenced
59    fn get_inputs(&self) -> HashSet<ResolvedVarnode>;
60    /// Get a hashset of the addresses written by this trace. The values returned in this hashset
61    /// are fully modeled: a read from a given varnode will evaluate to its value at the stage in
62    /// the computation that the read was performed. Because of this, these should always be read
63    /// from the `State` returned by `get_final_state`, as it is guaranteed to have a handle to
64    /// all intermediate spaces that may be referenced
65    fn get_outputs(&self) -> HashSet<ResolvedVarnode>;
66
67    ///`jingle` supports some rudimentary modeling of control flow; this will return a bitvector
68    /// encapsulating the possible end-of-block behaviors of this trace
69    fn get_branch_constraint(&self) -> &BranchConstraint;
70
71    /// SLEIGH models instructions using many address spaces, some of which do not map directly to
72    /// architectural spaces. For instance, the `unique` space is used as an intra-instruction
73    /// "scratch pad" for intermediate results and is explicitly cleared between each instruction.
74    /// Therefore, it is often useful to filter a varnode by whether it references an architectural
75    /// space, since we do not want to constrain spaces like `unique`.
76    fn should_varnode_constrain(&self, v: &ResolvedVarnode) -> bool {
77        match v {
78            Direct(d) => self
79                .get_final_state()
80                .arch_info()
81                .get_space(d.space_index())
82                .map(|o| o._type == SpaceType::IPTR_PROCESSOR)
83                .unwrap_or(false),
84            Indirect(_) => true,
85        }
86    }
87
88    /// Returns a `Bool` assertion that `self` upholds the postconditions of `other`.
89    /// This is done by iterating over all fully-modeled constraining outputs of `other` and
90    /// enforcing that the same locations in `self` are equal.
91    /// In our procedure, this is only ever called on contexts that we have already verified write
92    /// to all outputs that `other` did, eliminating the risk of spurious false positives
93    fn upholds_postcondition<T: ModelingContext>(&self, other: &T) -> Result<Bool, JingleError> {
94        let mut output_terms = vec![];
95        for vn in other
96            .get_outputs()
97            .iter()
98            .filter(|v| self.should_varnode_constrain(v))
99        {
100            let ours = self.get_final_state().read_resolved(vn)?;
101            let other_bv = other.get_final_state().read_resolved(vn)?;
102            output_terms.push(ours.eq(&other_bv).simplify());
103            if let Indirect(a) = vn {
104                let ours = self.get_final_state().read_varnode(&a.pointer_location)?;
105                let other = other.get_final_state().read_varnode(&a.pointer_location)?;
106                output_terms.push(ours.eq(&other).simplify());
107            }
108        }
109        let imp_terms: Vec<&Bool> = output_terms.iter().collect();
110        let outputs_pairwise_equal = Bool::and(imp_terms.as_slice());
111        Ok(outputs_pairwise_equal)
112    }
113
114    /// Returns an assertion that the final state of `self` and the first state of `other` are
115    /// equal. This allows for concatenating two traces into one for the purposes of modeling.
116    fn assert_concat<T: ModelingContext>(&self, other: &T) -> Result<Bool, JingleError> {
117        self.get_final_state()._eq(other.get_original_state())
118    }
119
120    /// Returns an assertion that `other`'s end-branch behavior is able to branch to the same
121    /// destination as `self`, given that `self` has branching behavior.
122    /// todo: should swap `self` and `other` to make this align better with `upholds_postcondition`
123    #[deprecated]
124    #[expect(deprecated)]
125    fn branch_comparison<T: ModelingContext>(
126        &self,
127        other: &T,
128    ) -> Result<Option<Bool>, JingleError> {
129        if !self.get_branch_constraint().has_branch() {
130            Ok(None)
131        } else {
132            if !self.get_branch_constraint().conditional_branches.is_empty()
133                || !other
134                    .get_branch_constraint()
135                    .conditional_branches
136                    .is_empty()
137            {
138                return Ok(Some(Bool::from_bool(false)));
139            }
140            let self_bv = self.get_branch_constraint().build_bv(self)?;
141            let other_bv = other.get_branch_constraint().build_bv(other)?;
142            let self_bv = zext_to_match(self_bv, &other_bv);
143            let other_bv = zext_to_match(other_bv, &self_bv);
144            let self_bv_metadata = self.get_branch_constraint().build_bv_metadata(self)?;
145            let other_bv_metadata = other.get_branch_constraint().build_bv_metadata(other)?;
146            let self_bv_metadata =
147                zext_to_match(self_bv_metadata.simplify(), &other_bv_metadata.simplify());
148            let other_bv_metadata = zext_to_match(other_bv_metadata, &self_bv_metadata);
149            Ok(Some(Bool::and(&[
150                self_bv._eq(&other_bv).simplify(),
151                self_bv_metadata._eq(&other_bv_metadata).simplify(),
152            ])))
153        }
154    }
155
156    /// Returns a `Bool` assertion that the given trace's end-branch behavior is able to
157    /// branch to the given `u64`.
158    #[expect(deprecated)]
159    fn can_branch_to_address(&self, addr: u64) -> Result<Bool, JingleError> {
160        let branch_constraint = self.get_branch_constraint().build_bv(self)?;
161        let addr_bv = BV::from_i64(addr as i64, branch_constraint.get_size());
162        Ok(branch_constraint._eq(&addr_bv))
163    }
164}
165
166/// This trait is used for types that build modeling contexts. This could maybe be a single
167/// struct instead of a trait.
168/// The helper methods in here allow for parsing pcode operations into z3 formulae, and
169/// automatically tracking the inputs/outputs of each operation and traces composed thereof
170pub(crate) trait TranslationContext: ModelingContext {
171    /// Adds a [GeneralizedVarNode] to the "input care set" for this operation.
172    /// This is usually used for asserting equality of all input varnodes when
173    /// comparing operations
174    fn track_input(&mut self, input: &ResolvedVarnode);
175
176    /// Adds a [GeneralizedVarNode] to the "input care set" for this operation.
177    /// This is usually used for asserting post-equality and pre-inequality
178    /// of all output [GeneralizedVarNode]s when comparing operations
179    fn track_output(&mut self, output: &ResolvedVarnode);
180
181    /// Get a mutable handle to the "lastest" state
182    fn get_final_state_mut(&mut self) -> &mut State;
183
184    /// Get the helper object for encapsulating branch behavior
185    fn get_branch_builder(&mut self) -> &mut BranchConstraint;
186
187    /// A helper function to both read and track an input [VarNode].
188    fn read_and_track(&mut self, gen_varnode: GeneralizedVarNode) -> Result<BV, JingleError> {
189        match gen_varnode {
190            GeneralizedVarNode::Direct(d) => {
191                self.track_input(&Direct(d.clone()));
192                self.get_final_state().read_varnode(&d)
193            }
194            GeneralizedVarNode::Indirect(indirect) => {
195                self.track_input(&Direct(indirect.pointer_location().clone()));
196                let pointer = self
197                    .get_final_state()
198                    .read_varnode(indirect.pointer_location())?
199                    .clone();
200                self.track_input(&Indirect(ResolvedIndirectVarNode {
201                    pointer,
202                    pointer_location: indirect.pointer_location().clone(),
203                    access_size_bytes: indirect.access_size_bytes(),
204                    pointer_space_idx: indirect.pointer_space_index(),
205                }));
206                self.get_final_state().read_varnode_indirect(&indirect)
207            }
208        }
209    }
210
211    fn write(&mut self, r#gen: &GeneralizedVarNode, val: BV) -> Result<(), JingleError> {
212        match r#gen {
213            GeneralizedVarNode::Direct(d) => {
214                self.track_output(&Direct(d.clone()));
215                self.get_final_state_mut().write_varnode(d, val)?;
216            }
217            GeneralizedVarNode::Indirect(indirect) => {
218                let pointer = self.read_and_track(indirect.pointer_location().clone().into())?;
219                self.track_output(&Indirect(ResolvedIndirectVarNode {
220                    pointer,
221                    pointer_location: indirect.pointer_location().clone(),
222                    access_size_bytes: indirect.access_size_bytes(),
223                    pointer_space_idx: indirect.pointer_space_index(),
224                }));
225                self.get_final_state_mut()
226                    .write_varnode_indirect(indirect, val)?;
227            }
228        }
229        Ok(())
230    }
231
232    /// Apply the updates of a [PcodeOperation] on top of this context.
233    #[instrument(skip_all)]
234    fn model_pcode_op(&mut self, op: &PcodeOperation) -> Result<(), JingleError>
235    where
236        Self: Sized,
237    {
238        match op {
239            PcodeOperation::Copy { input, output } => {
240                let val = self.read_and_track(input.into())?;
241                let metadata = self.get_original_state().read_varnode_metadata(input)?;
242                self.get_final_state_mut()
243                    .write_varnode_metadata(output, metadata)?;
244                self.write(&output.into(), val)
245            }
246            PcodeOperation::IntZExt { input, output } => {
247                let diff = (output.size() - input.size()) as u32;
248                let val = self.read_and_track(input.into())?;
249                let zext = val.zero_ext(diff * 8);
250                self.write(&output.into(), zext)
251            }
252            PcodeOperation::IntSExt { input, output } => {
253                let diff = (output.size() - input.size()) as u32;
254                let val = self.read_and_track(input.into())?;
255                let zext = val.sign_ext(diff * 8);
256                self.write(&output.into(), zext)
257            }
258            PcodeOperation::Store { output, input } => {
259                // read the input we need to STORE
260                let bv = self.read_and_track(input.into())?;
261                // write the input to the proper space, at the offset we read
262                self.write(&output.into(), bv)
263            }
264            PcodeOperation::Load { input, output } => {
265                // read the input we need to LOAD
266                let bv = self.read_and_track(input.into())?;
267                // read the stored offset for the LOAD destination
268                // write the loaded input to the output
269                self.write(&output.into(), bv)
270            }
271            PcodeOperation::IntAdd {
272                input0,
273                input1,
274                output,
275            } => {
276                let bv1 = self.read_and_track(input0.into())?;
277                let bv2 = self.read_and_track(input1.into())?;
278                let add = bv1 + bv2;
279                self.write(&output.into(), add)
280            }
281            PcodeOperation::IntSub {
282                input0,
283                input1,
284                output,
285            } => {
286                let bv1 = self.read_and_track(input0.into())?;
287                let bv2 = self.read_and_track(input1.into())?;
288                let add = bv1 - bv2;
289                self.write(&output.into(), add)
290            }
291            PcodeOperation::IntAnd {
292                input0,
293                input1,
294                output,
295            } => {
296                let bv1 = self.read_and_track(input0.into())?;
297                let bv2 = self.read_and_track(input1.into())?;
298                let and = bv1.bvand(&bv2);
299                self.write(&output.into(), and)
300            }
301            PcodeOperation::IntXor {
302                input0,
303                input1,
304                output,
305            } => {
306                let bv1 = self.read_and_track(input0.into())?;
307                let bv2 = self.read_and_track(input1.into())?;
308                let and = bv1.bvxor(&bv2);
309                self.write(&output.into(), and)
310            }
311            PcodeOperation::IntOr {
312                input0,
313                input1,
314                output,
315            } => {
316                let bv1 = self.read_and_track(input0.into())?;
317                let bv2 = self.read_and_track(input1.into())?;
318                let or = bv1.bvor(&bv2);
319                self.write(&output.into(), or)
320            }
321            PcodeOperation::IntNegate { input, output } => {
322                let bv = self.read_and_track(input.into())?;
323                let neg = bv.neg();
324                self.write(&output.into(), neg)
325            }
326            PcodeOperation::IntMult {
327                input0,
328                input1,
329                output,
330            } => {
331                let bv1 = self.read_and_track(input0.into())?;
332                let bv2 = self.read_and_track(input1.into())?;
333                let mul = bv1.bvmul(&bv2);
334                self.write(&output.into(), mul)
335            }
336            PcodeOperation::IntDiv {
337                input0,
338                input1,
339                output,
340            } => {
341                let bv1 = self.read_and_track(input0.into())?;
342                let bv2 = self.read_and_track(input1.into())?;
343                let mul = bv1.bvudiv(&bv2);
344                self.write(&output.into(), mul)
345            }
346            PcodeOperation::IntSignedDiv {
347                input0,
348                input1,
349                output,
350            } => {
351                let bv1 = self.read_and_track(input0.into())?;
352                let bv2 = self.read_and_track(input1.into())?;
353                let mul = bv1.bvsdiv(&bv2);
354                self.write(&output.into(), mul)
355            }
356            PcodeOperation::IntRem {
357                input0,
358                input1,
359                output,
360            } => {
361                let bv1 = self.read_and_track(input0.into())?;
362                let bv2 = self.read_and_track(input1.into())?;
363                let mul = bv1.bvurem(&bv2);
364                self.write(&output.into(), mul)
365            }
366            PcodeOperation::IntSignedRem {
367                input0,
368                input1,
369                output,
370            } => {
371                let bv1 = self.read_and_track(input0.into())?;
372                let bv2 = self.read_and_track(input1.into())?;
373                let mul = bv1.bvsrem(&bv2);
374                self.write(&output.into(), mul)
375            }
376            PcodeOperation::IntRightShift {
377                input0,
378                input1,
379                output,
380            } => {
381                let bv1 = self.read_and_track(input0.into())?;
382                let mut bv2 = self.read_and_track(input1.into())?;
383                match bv1.get_size().cmp(&bv2.get_size()) {
384                    Ordering::Less => bv2 = bv2.extract(bv1.get_size() - 1, 0),
385                    Ordering::Greater => bv2 = bv2.zero_ext(bv1.get_size() - bv2.get_size()),
386                    _ => {}
387                }
388                let rshift = bv1.bvlshr(&bv2);
389                self.write(&output.into(), rshift)
390            }
391            PcodeOperation::IntSignedRightShift {
392                input0,
393                input1,
394                output,
395            } => {
396                let bv1 = self.read_and_track(input0.into())?;
397                let mut bv2 = self.read_and_track(input1.into())?;
398                match bv1.get_size().cmp(&bv2.get_size()) {
399                    Ordering::Less => bv2 = bv2.extract(bv1.get_size() - 1, 0),
400                    Ordering::Greater => bv2 = bv2.zero_ext(bv1.get_size() - bv2.get_size()),
401                    _ => {}
402                }
403                let rshift = bv1.bvashr(&bv2);
404                self.write(&output.into(), rshift)
405            }
406            PcodeOperation::IntLeftShift {
407                input0,
408                input1,
409                output,
410            } => {
411                let mut bv1 = self.read_and_track(input0.into())?;
412                let mut bv2 = self.read_and_track(input1.into())?;
413                match bv1.get_size().cmp(&bv2.get_size()) {
414                    Ordering::Less => bv1 = bv1.zero_ext(bv2.get_size() - bv1.get_size()),
415                    Ordering::Greater => bv2 = bv2.zero_ext(bv1.get_size() - bv2.get_size()),
416                    _ => {}
417                }
418                let lshift = bv1.bvshl(&bv2);
419                self.write(&output.into(), lshift)
420            }
421            PcodeOperation::IntCarry {
422                input0,
423                input1,
424                output,
425            } => {
426                let in0 = self.read_and_track(input0.into())?;
427                let in1 = self.read_and_track(input1.into())?;
428                // bool arg seems to be for whether this check is signed
429                let carry_bool = in0.bvadd_no_overflow(&in1, false);
430                let out_bv = carry_bool.ite(&BV::from_i64(0, 8), &BV::from_i64(1, 8));
431                self.write(&output.into(), out_bv)
432            }
433            PcodeOperation::IntSignedCarry {
434                input0,
435                input1,
436                output,
437            } => {
438                let in0 = self.read_and_track(input0.into())?;
439                let in1 = self.read_and_track(input1.into())?;
440                // bool arg seems to be for whether this check is signed
441                let carry_bool = in0.bvadd_no_overflow(&in1, true);
442                let out_bv = carry_bool.ite(&BV::from_i64(0, 8), &BV::from_i64(1, 8));
443                self.write(&output.into(), out_bv)
444            }
445            PcodeOperation::IntSignedBorrow {
446                input0,
447                input1,
448                output,
449            } => {
450                let in0 = self.read_and_track(input0.into())?;
451                let in1 = self.read_and_track(input1.into())?;
452                // todo: need to do some experimentation as to what the intended
453                // meaning of "overflow" is in sleigh vs what it means in z3
454                let borrow_bool = in0.bvsub_no_underflow(&in1, true);
455                let out_bv = borrow_bool.ite(&BV::from_i64(0, 8), &BV::from_i64(1, 8));
456                self.write(&output.into(), out_bv)
457            }
458            PcodeOperation::Int2Comp { input, output } => {
459                let in0 = self.read_and_track(input.into())?;
460                let flipped = in0.bvneg().add(BV::from_u64(1, in0.get_size()));
461                self.write(&output.into(), flipped)
462            }
463            PcodeOperation::IntSignedLess {
464                input0,
465                input1,
466                output,
467            } => {
468                let in0 = self.read_and_track(input0.into())?;
469                let in1 = self.read_and_track(input1.into())?;
470                let out_bool = in0.bvslt(&in1);
471                let out_bv = out_bool.ite(&BV::from_i64(1, 8), &BV::from_i64(0, 8));
472                self.write(&output.into(), out_bv)
473            }
474            PcodeOperation::IntSignedLessEqual {
475                input0,
476                input1,
477                output,
478            } => {
479                let in0 = self.read_and_track(input0.into())?;
480                let in1 = self.read_and_track(input1.into())?;
481                let out_bool = in0.bvsle(&in1);
482                let out_bv = out_bool.ite(&BV::from_i64(1, 8), &BV::from_i64(0, 8));
483                self.write(&output.into(), out_bv)
484            }
485            PcodeOperation::IntLess {
486                input0,
487                input1,
488                output,
489            } => {
490                let in0 = self.read_and_track(input0.into())?;
491                let in1 = self.read_and_track(input1.into())?;
492                let out_bool = in0.bvult(&in1);
493                let out_bv = out_bool.ite(&BV::from_i64(1, 8), &BV::from_i64(0, 8));
494                self.write(&output.into(), out_bv)
495            }
496            PcodeOperation::IntLessEqual {
497                input0,
498                input1,
499                output,
500            } => {
501                let in0 = self.read_and_track(input0.into())?;
502                let in1 = self.read_and_track(input1.into())?;
503                let out_bool = in0.bvule(&in1);
504                let out_bv = out_bool.ite(&BV::from_i64(1, 8), &BV::from_i64(0, 8));
505                self.write(&output.into(), out_bv)
506            }
507            PcodeOperation::IntEqual {
508                input0,
509                input1,
510                output,
511            } => {
512                let in0 = self.read_and_track(input0.into())?;
513                let in1 = self.read_and_track(input1.into())?;
514                let outsize = output.size() as u32;
515                let out_bool = in0.eq(&in1);
516                let out_bv =
517                    out_bool.ite(&BV::from_i64(1, outsize * 8), &BV::from_i64(0, outsize * 8));
518                self.write(&output.into(), out_bv)
519            }
520            PcodeOperation::IntNotEqual {
521                input0,
522                input1,
523                output,
524            } => {
525                let in0 = self.read_and_track(input0.into())?;
526                let in1 = self.read_and_track(input1.into())?;
527                let outsize = output.size() as u32;
528                let out_bool = in0.eq(&in1).not();
529                let out_bv =
530                    out_bool.ite(&BV::from_i64(1, outsize * 8), &BV::from_i64(0, outsize * 8));
531                self.write(&output.into(), out_bv)
532            }
533            PcodeOperation::BoolAnd {
534                input0,
535                input1,
536                output,
537            } => {
538                let i0 = self.read_and_track(input0.into())?;
539                let i1 = self.read_and_track(input1.into())?;
540                let result = i0.bvand(&i1).bvand(1);
541                self.write(&output.into(), result)
542            }
543            PcodeOperation::BoolNegate { input, output } => {
544                let val = self.read_and_track(input.into())?;
545                let negated = val.bvneg().bvand(1);
546                self.write(&output.into(), negated)
547            }
548            PcodeOperation::BoolOr {
549                input0,
550                input1,
551                output,
552            } => {
553                let i0 = self.read_and_track(input0.into())?;
554                let i1 = self.read_and_track(input1.into())?;
555                let result = (i0 | i1) & 1;
556                self.write(&output.into(), result)
557            }
558            PcodeOperation::BoolXor {
559                input0,
560                input1,
561                output,
562            } => {
563                let i0 = self.read_and_track(input0.into())?;
564                let i1 = self.read_and_track(input1.into())?;
565                let result = i0.bvxor(&i1).bvand(1);
566                self.write(&output.into(), result)
567            }
568            PcodeOperation::PopCount { input, output } => {
569                let size = output.size() as u32;
570                let in0 = self.read_and_track(input.into())?;
571                let mut outbv = BV::from_i64(0, output.size() as u32 * 8);
572                for i in 0..size * 8 {
573                    let extract = in0.extract(i, i);
574                    let extend = extract.zero_ext((size * 8) - 1);
575                    outbv = outbv.bvadd(&extend);
576                }
577
578                self.write(&output.into(), outbv)
579            }
580            PcodeOperation::Branch { input } => {
581                self.get_branch_builder()
582                    .set_last(&GeneralizedVarNode::from(input));
583                self.read_and_track(GeneralizedVarNode::from(input))?;
584                Ok(())
585            }
586            PcodeOperation::BranchInd { input } => {
587                self.get_branch_builder()
588                    .set_last(&GeneralizedVarNode::from(input));
589                self.read_and_track(GeneralizedVarNode::from(input.pointer_location()))?;
590                Ok(())
591            }
592            PcodeOperation::Call { dest, .. } => {
593                self.get_branch_builder().set_last(&dest.into());
594                self.read_and_track(dest.into())?;
595                Ok(())
596            }
597            PcodeOperation::CBranch { input0, input1 } => {
598                self.get_branch_builder()
599                    .push_conditional(&BlockConditionalBranchInfo {
600                        condition: input1.clone(),
601                        destination: input0.into(),
602                    });
603                self.read_and_track(input0.into())?;
604                self.read_and_track(input1.into())?;
605                Ok(())
606            }
607            PcodeOperation::SubPiece {
608                input0,
609                input1,
610                output,
611            } => {
612                let bv0 = self.read_and_track(input0.into())?;
613                // sleigh asserts that input1 is a constant
614                let input_low_byte = input1.offset() as u32;
615                let input_size = (input0.size() as u32) - input_low_byte;
616                let output_size = output.size() as u32;
617                let size = min(input_size, output_size);
618                let input = bv0.extract((input_low_byte + size) * 8 - 1, input_low_byte * 8);
619                match size.cmp(&output_size) {
620                    Ordering::Less => {
621                        self.write(&output.into(), input.zero_ext((output_size - size) * 8))
622                    }
623                    Ordering::Greater => {
624                        self.write(&output.into(), input.extract(output_size * 8 - 1, 0))
625                    }
626                    Ordering::Equal => self.write(&output.into(), input),
627                }
628            }
629            PcodeOperation::CallOther { inputs, output, .. } => {
630                let mut hasher = DefaultHasher::new();
631                for vn in inputs {
632                    vn.hash(&mut hasher);
633                }
634                let hash = hasher.finish();
635                for input in inputs.iter() {
636                    self.read_and_track(input.into())?;
637                }
638                let size = self
639                    .get_final_state()
640                    .get_default_code_space_info()
641                    .index_size_bytes;
642                let hash_vn = create_varnode(self.get_arch_info(), "const", hash, size)?;
643                let metadata = self
644                    .get_final_state()
645                    .immediate_metadata_array(true, hash_vn.size());
646                self.get_final_state_mut()
647                    .write_varnode_metadata(&hash_vn, metadata)?;
648                self.get_branch_builder().set_last(&hash_vn.into());
649                if let Some(out) = output {
650                    let size = out.size() * 8;
651                    let hash_bv = BV::from_u64(hash, size as u32);
652                    let metadata = self
653                        .get_final_state()
654                        .immediate_metadata_array(true, out.size());
655                    self.get_final_state_mut()
656                        .write_varnode_metadata(out, metadata)?;
657                    self.write(&out.into(), hash_bv)?;
658                }
659                Ok(())
660            }
661            PcodeOperation::CallInd { input } => {
662                self.get_branch_builder()
663                    .set_last(&GeneralizedVarNode::from(input));
664                self.read_and_track(GeneralizedVarNode::from(input.pointer_location()))?;
665                Ok(())
666            }
667            PcodeOperation::Return { input } => {
668                self.get_branch_builder()
669                    .set_last(&GeneralizedVarNode::from(input));
670                self.read_and_track(GeneralizedVarNode::from(input.pointer_location()))?;
671                Ok(())
672            }
673            v => Err(JingleError::UnmodeledInstruction(Box::new(v.clone()))),
674        }
675    }
676}
677
678fn zext_to_match(bv1: BV, bv2: &BV) -> BV {
679    if bv1.get_size() < bv2.get_size() {
680        bv1.zero_ext(bv2.get_size() - bv1.get_size())
681    } else {
682        bv1
683    }
684}