//! CUBELinux-2 DBT (dynamic binary translation) runtime. //! //! Per the PDF (Package 4, §549): *"cubedbt: a runtime that reads CZYX‑stored //! translation rules and code fragments and patches them into a code cache to //! execute mimicked behavior."* //! //! This crate is the substrate for the PDF's "watch a binary, archive its //! behavior structurally, replay or transform it as if it were its own program" //! story (§554). It is deliberately dependency-free and operates on the real //! `CubeStore` / `CodeCell` / `Vm` types from the rest of the workspace, so it //! is exercisable today on `HashBackend` and slots into `ConcurrentStore` //! later without an API change. //! //! Model //! ----- //! * A **translation rule** is a CZYX record (`Kind::Variant`) whose body is a //! serialized [`TranslationRule`]. It names an *op kind* it can replace and //! carries a *replacement fragment* (a `Vec`). The rule record links to //! the original code cell it may substitute (the association edge). //! * A **code cache** is the patched working set: `patch()` rewrites an //! original `CodeCell`'s bytecode under the eligible rules and writes the //! result back as a new record (preserving the original header's kind/title //! and behavior descriptors, so it runs through the same `Vm`). //! * A **DBT runtime** discovers rules in a store and can `mimic` a target //! coordinate: fetch original → apply rules → run the patched version in the //! `Vm`, returning the `RunResult`. use cubecode::{opcode::Op, CodeCell, Kind, Vm}; use cubecoords::{CubeHeader, Czyx}; use cubestore::{CubeBackend, CubeStore, HashBackend}; /// `C` axis band where DBT translation rules are stored, kept distinct from the /// `c210` OS-kernel band (`cubecode::C_OS_KERNEL`) and the `c200` snapshot data. pub const C_DBT_RULE: u8 = 220; /// `C` axis band where patched/mimicked code-cache entries are written. pub const C_DBT_CACHE: u8 = 221; /// The `Op` kinds a translation rule can target. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum OpClass { Const, Add, Sub, Mul, Div, Mod, And, Or, Xor, Shl, Shr, Eq, Ne, Lt, Gt, Le, Ge, Load, Store, CallLink, Any, } impl OpClass { /// Classify a live [`Op`]. pub fn of(op: &Op) -> OpClass { match op { Op::Const(_) => OpClass::Const, Op::Add => OpClass::Add, Op::Sub => OpClass::Sub, Op::Mul => OpClass::Mul, Op::Div => OpClass::Div, Op::Mod => OpClass::Mod, Op::And => OpClass::And, Op::Or => OpClass::Or, Op::Xor => OpClass::Xor, Op::Shl => OpClass::Shl, Op::Shr => OpClass::Shr, Op::Eq => OpClass::Eq, Op::Ne => OpClass::Ne, Op::Lt => OpClass::Lt, Op::Gt => OpClass::Gt, Op::Le => OpClass::Le, Op::Ge => OpClass::Ge, Op::Load(_) => OpClass::Load, Op::Store(_) => OpClass::Store, Op::CallLink(_) => OpClass::CallLink, _ => OpClass::Any, } } } /// A translation rule: a named, CZYX-addressed mapping from an op class to a /// replacement bytecode fragment. Serialized into a rule record's body. #[derive(Clone, Debug, PartialEq, Eq)] pub struct TranslationRule { /// Human name (also the rule record's title). pub name: String, /// Which op class this rule can substitute. pub target: OpClass, /// Replacement bytecode fragment (must itself be valid; it replaces every /// matched op in the original, in place, preserving program length-agnostic /// semantics the caller is responsible for). pub fragment: Vec, } impl TranslationRule { /// Serialize to a stable byte form (name length-prefixed, target byte, /// fragment as opcode codec). No external deps. pub fn to_bytes(&self) -> Vec { let mut out = Vec::new(); let nb = self.name.as_bytes(); out.push(nb.len() as u8); out.extend_from_slice(nb); out.push(self.target as u8); out.push(self.fragment.len() as u8); for op in &self.fragment { out.extend_from_slice(&cubecode::opcode::encode(&[*op])); } out } /// Inverse of [`to_bytes`]. Returns `None` on any malformed input. pub fn from_bytes(b: &[u8]) -> Option { let mut i = 0; let nlen = *b.get(i)? as usize; i += 1; if i + nlen > b.len() { return None; } let name = String::from_utf8(b[i..i + nlen].to_vec()).ok()?; i += nlen; let target = *b.get(i)?; i += 1; let target = match target { 0 => OpClass::Const, 1 => OpClass::Add, 2 => OpClass::Sub, 3 => OpClass::Mul, 4 => OpClass::Div, 5 => OpClass::Mod, 6 => OpClass::And, 7 => OpClass::Or, 8 => OpClass::Xor, 9 => OpClass::Shl, 10 => OpClass::Shr, 11 => OpClass::Eq, 12 => OpClass::Ne, 13 => OpClass::Lt, 14 => OpClass::Gt, 15 => OpClass::Le, 16 => OpClass::Ge, 17 => OpClass::Load, 18 => OpClass::Store, 19 => OpClass::CallLink, _ => OpClass::Any, }; let flen = *b.get(i)? as usize; i += 1; let mut fragment = Vec::new(); for _ in 0..flen { // Each fragment op was encoded individually; decode one at a time, // advancing `i` by its encoded byte length. let one = decode_one(&b[i..])?; i += one.len; fragment.push(one.op); } Some(TranslationRule { name, target, fragment, }) } } /// One decoded opcode plus its encoded length, used by `from_bytes`. struct One { op: Op, len: usize, } fn decode_one(b: &[u8]) -> Option { let op = cubecode::opcode::decode(b).ok()?; let op = op.into_iter().next()?; let len = cubecode::opcode::encode(&[op]).len(); Some(One { op, len }) } /// A patched working set ("code cache") inside a [`CubeStore`]. /// /// `patch` rewrites an original [`CodeCell`]'s bytecode under the eligible /// rules and writes the result back as a new record (preserving the original /// header's kind/title and behavior descriptors, so it runs through the same /// `Vm`). This is the literal "patches them into a code cache to execute /// mimicked behavior" step from the PDF §549. pub struct CodeCache { store: CubeStore, /// Coordinate of the next cache slot to allocate. next_x: u8, } impl CodeCache { /// Build an empty cache over a fresh in-memory backend. pub fn new() -> Self { CodeCache { store: CubeStore::new(HashBackend::new()), next_x: 1, } } /// Patch `original` under `rules`: every op whose class matches a rule's /// target (or whose rule target is `Any`) is replaced by that rule's /// fragment. The rewritten cell is stored at a fresh cache coordinate and /// returned (with its new label). The original store is untouched. pub fn patch(&mut self, original: &CodeCell, rules: &[TranslationRule]) -> CodeCell { let mut patched: Vec = Vec::with_capacity(original.code.len()); for op in &original.code { let class = OpClass::of(op); let mut applied = false; for r in rules { if r.target == class || r.target == OpClass::Any { patched.extend_from_slice(&r.fragment); applied = true; break; } } if !applied { patched.push(*op); } } let label = Czyx::new(C_DBT_CACHE, 1, 1, self.next_x); self.next_x = self.next_x.wrapping_add(1).max(1); let (h, body) = cache_header_for(original, &cubecode::opcode::encode(&patched)); self.store.put_record(label, &h, &body); CodeCell::from_record(label, &h, &body).expect("patched cell is always valid bytecode") } /// Run a patched cache entry in the VM, returning its result. pub fn run(&self, cell: &CodeCell) -> cubecode::RunResult { let mut vm = Vm::new(self.store.clone()); vm.run(cell.label) } /// Borrow the backing store (e.g. to persist or inspect). pub fn store(&self) -> &CubeStore { &self.store } } impl Default for CodeCache { fn default() -> Self { Self::new() } } /// Build a cache record header that preserves the original's kind/title and /// behavior descriptors, but marks it a `Variant` (a mimicked implementation). fn cache_header_for(original: &CodeCell, body: &[u8]) -> (CubeHeader, Vec) { let mut h = CubeHeader::new(); h.title = original.name().map(|s| format!("mimic:{}", s)); h.doc_type = Some(Kind::Variant.as_str().into()); h.size_bytes = Some(body.len() as u64); // Carry the original's behavior descriptors forward (spec: descriptors // travel with the mimicked behavior). h.flags.0 |= original.header.flags.0 & cubecode::HEADER_FLAG_BEHAVIOR; h.refresh_flags(); (h, body.to_vec()) } /// The DBT runtime: discovers CZYX-stored translation rules and can `mimic` a /// target code cell by patching it under those rules and running the result. pub struct DbRuntime { store: CubeStore, } impl DbRuntime { /// Wrap a store that already contains rule records (or will, via /// [`store_rule`]). The runtime is read-only over this store. pub fn new(store: CubeStore) -> Self { DbRuntime { store } } /// Collect every translation rule currently in the `c220` rule band. pub fn discover_rules(&self) -> Vec { let mut rules = Vec::new(); for k in self.store.keys() { if k.c != C_DBT_RULE { continue; } if let Some((_, body)) = self.store.get_record(&k) { if let Some(r) = TranslationRule::from_bytes(&body) { rules.push(r); } } } rules } /// Fetch a code cell by coordinate from the wrapped store. pub fn fetch(&self, label: Czyx) -> Option { let (h, b) = self.store.get_record(&label)?; CodeCell::from_record(label, &h, &b) } /// Mimic `target`: fetch it, apply all discovered rules, patch into a fresh /// cache, and run the patched version — returning the `RunResult`. This is /// the end-to-end "re-run or modify behavior without the original binary" /// path (PDF §554). pub fn mimic(&self, target: Czyx) -> Option { let original = self.fetch(target)?; let rules = self.discover_rules(); let mut cache = CodeCache::new(); let patched = cache.patch(&original, &rules); Some(cache.run(&patched)) } } /// Store a translation rule into the `c220` rule band at a fresh coordinate. /// Returns the coordinate it was written to. pub fn store_rule(store: &mut CubeStore, rule: &TranslationRule, x: u8) -> Czyx { let label = Czyx::new(C_DBT_RULE, 1, 1, x); let mut h = CubeHeader::new(); h.title = Some(rule.name.clone()); h.doc_type = Some(Kind::Variant.as_str().into()); h.size_bytes = Some(rule.to_bytes().len() as u64); h.refresh_flags(); store.put_record(label, &h, &rule.to_bytes()); label } #[cfg(test)] mod tests { use super::*; use cubecode::opcode::Op; use cubecode::RunResult; use cubecoords::Czyx; fn sample_cell(coord: Czyx, code: Vec) -> CodeCell { let mut h = CubeHeader::new(); h.title = Some("sample".into()); h.doc_type = Some(Kind::Fn.as_str().into()); h.refresh_flags(); CodeCell::from_record(coord, &h, &cubecode::opcode::encode(&code)) .expect("sample is valid bytecode") } #[test] fn rule_round_trips_through_bytes() { let r = TranslationRule { name: "double-add".into(), target: OpClass::Add, fragment: vec![Op::Const(2), Op::Mul], }; let bytes = r.to_bytes(); let back = TranslationRule::from_bytes(&bytes).expect("rule decodes"); assert_eq!(r, back); } #[test] fn cache_patches_and_runs() { // Original: Const 3, Const 4, Add, Halt => 3+4 = 7. let orig = sample_cell( Czyx::new(1, 1, 1, 1), vec![Op::Const(3), Op::Const(4), Op::Add, Op::Halt], ); // Rule: replace `Add` with `Const 2, Mul` => (a)*(2). With a=3,b=4: // naive in-place substitution yields Const3, Const4, Const2, Mul => // 4*2 = 8. This proves the patched fragment is what runs. let rule = TranslationRule { name: "mul-by-2".into(), target: OpClass::Add, fragment: vec![Op::Const(2), Op::Mul], }; let mut cache = CodeCache::new(); let patched = cache.patch(&orig, &[rule]); assert_eq!(patched.kind(), Kind::Variant, "cache entry is a Variant"); match cache.run(&patched) { RunResult::Halted { top: Some(v) } => assert_eq!(v, 8, "patched semantics differ"), other => panic!("patched run failed: {:?}", other), } } #[test] fn runtime_mimic_end_to_end() { let mut store = CubeStore::new(HashBackend::new()); // Original target at c1. let target = Czyx::new(1, 1, 1, 1); let orig = sample_cell(target, vec![Op::Const(10), Op::Const(5), Op::Sub, Op::Halt]); store.put_record(orig.label, &orig.header, &orig.body()); // Rule in the c220 band: replace Sub with Add (10+5=15 instead of 5). let rule = TranslationRule { name: "sub->add".into(), target: OpClass::Sub, fragment: vec![Op::Add], }; store_rule(&mut store, &rule, 1); let rt = DbRuntime::new(store); let rules = rt.discover_rules(); assert_eq!(rules.len(), 1, "rule discovered in c220 band"); match rt.mimic(target) { Some(RunResult::Halted { top: Some(v) }) => assert_eq!(v, 15, "mimic applied rule"), other => panic!("mimic failed: {:?}", other), } } #[test] fn mimic_without_rules_matches_original() { let mut store = CubeStore::new(HashBackend::new()); let target = Czyx::new(1, 1, 1, 2); let orig = sample_cell(target, vec![Op::Const(7), Op::Const(2), Op::Mul, Op::Halt]); store.put_record(orig.label, &orig.header, &orig.body()); let rt = DbRuntime::new(store); match rt.mimic(target) { Some(RunResult::Halted { top: Some(v) }) => assert_eq!(v, 14), other => panic!("mimic without rules should run original: {:?}", other), } } }