//! Additive SQL views over the Phase-3 ingest tables. //! //! These views are created when `--analyze=sql` or `--analyze=both` is set. //! They are *not* a replacement for the Rust passes ([`crate::xref`], //! [`crate::func`]) — those still own data-ref resolution and prologue //! pattern matching. The views cover the cleanly-relational parts: //! //! - branch xrefs (self-join on `instructions.target_hex`) //! - call graph + reachability (recursive CTE over `xrefs`) //! - convenience joins (function-first-instruction, imports-called) //! //! All views are read-only and stable across re-creation: dropping and //! recreating the database via [`crate::db::DbWriter::open_fresh`] re-runs //! these definitions. //! //! ## Cross-check semantics //! //! `v_branch_xrefs` is intended to produce *exactly* the same `(source, //! target, kind)` tuples as the Rust `xref.rs` first pass — given the same //! input image. [`crate::db::DbWriter::cross_check_branch_xrefs`] queries //! the symmetric difference and returns the row counts; both should be //! zero. A non-zero count means the formatter's `mnemonic` column or the //! kind-classification CASE drifted out of agreement with `xref.rs`, and //! is worth a one-line warning at log time. /// Every XDBF string side-by-side across the languages the title ships, so a /// piece of UI text can be looked up once and read in all locales. const V_XDBF_TEXT: &str = " CREATE OR REPLACE VIEW v_xdbf_text AS SELECT s.string_id, MAX(CASE WHEN s.language = 1 THEN s.value END) AS english, MAX(CASE WHEN s.language = 2 THEN s.value END) AS japanese, MAX(CASE WHEN s.language = 3 THEN s.value END) AS german, MAX(CASE WHEN s.language = 4 THEN s.value END) AS french, MAX(CASE WHEN s.language = 5 THEN s.value END) AS spanish, MAX(CASE WHEN s.language = 6 THEN s.value END) AS italian FROM xdbf_strings s GROUP BY s.string_id; "; /// Achievements joined to their three strings in every shipped language. const V_XDBF_ACHIEVEMENTS: &str = " CREATE OR REPLACE VIEW v_xdbf_achievements AS SELECT a.id, a.gamerscore, s.language, s.language_name, n.value AS name, u.value AS unlocked_desc, l.value AS locked_desc, a.image_id FROM xdbf_achievements a JOIN (SELECT DISTINCT language, language_name FROM xdbf_strings) s ON TRUE LEFT JOIN xdbf_strings n ON n.language = s.language AND n.string_id = a.label_id LEFT JOIN xdbf_strings u ON u.language = s.language AND u.string_id = a.description_id LEFT JOIN xdbf_strings l ON l.language = s.language AND l.string_id = a.unachieved_id; "; /// `(view_name, CREATE VIEW … SQL)` pairs in the order they must run. /// Later views may depend on earlier ones (e.g. `v_call_graph` reads /// `xrefs`, which is the Rust-pass table; `v_branch_xrefs` is independent). pub const ALL_VIEWS: &[(&str, &str)] = &[ ("v_branch_xrefs", V_BRANCH_XREFS), ("v_call_graph", V_CALL_GRAPH), ("v_reachability_from_entry", V_REACHABILITY_FROM_ENTRY), ( "v_indirect_reachability_from_entry", V_INDIRECT_REACHABILITY_FROM_ENTRY, ), ("v_function_first_instruction", V_FUNCTION_FIRST_INSTRUCTION), ("v_imports_called", V_IMPORTS_CALLED), ("v_xdbf_text", V_XDBF_TEXT), ("v_xdbf_achievements", V_XDBF_ACHIEVEMENTS), ("v_switch_cases", V_SWITCH_CASES), ("v_class_hierarchy", V_CLASS_HIERARCHY), ("v_class_methods", V_CLASS_METHODS), ("v_function_strings", V_FUNCTION_STRINGS), ]; /// Branch cross-references derived purely from `instructions.target_hex`. /// /// Mirrors the kind classification in [`crate::xref::collect_branch_target`] /// and the short tags returned by [`crate::xref::XrefKind::tag`] (which are /// what `xrefs.kind` actually stores): /// - I-form (`b`/`bl`/`ba`/`bla`): `bl`/`bla` → `"call"`, `b`/`ba` → `"j"` /// - B-form (`bc`/`bcl`/`bca`/`bcla`): always → `"br"` /// /// Indirect branches (`bclr`/`bcctr`) leave `target_hex` NULL and are /// excluded from this view by design. const V_BRANCH_XREFS: &str = " CREATE OR REPLACE VIEW v_branch_xrefs AS SELECT address AS source, target_hex AS target, CASE WHEN mnemonic IN ('bl', 'bla') THEN 'call' WHEN mnemonic IN ('b', 'ba') THEN 'j' WHEN mnemonic IN ('bc', 'bcl', 'bca', 'bcla') THEN 'br' ELSE 'br' END AS kind, mnemonic AS instruction, function AS source_func FROM instructions WHERE target_hex IS NOT NULL; "; /// Call-graph edges resolved against function names. /// /// Reads from `xrefs` (the Rust-pass table) — this is the canonical source /// for *all* edge kinds, including indirect/data; SQL can't reconstruct the /// data-ref edges cleanly because they require register tracking. For pure /// branch edges, `v_branch_xrefs` produces equivalent rows directly from /// `instructions`. const V_CALL_GRAPH: &str = " CREATE OR REPLACE VIEW v_call_graph AS SELECT x.source AS caller_addr, cf.name AS caller_name, x.target AS callee_addr, tf.name AS callee_name, x.kind AS edge_kind FROM xrefs x LEFT JOIN functions cf ON cf.address = x.source_func LEFT JOIN functions tf ON tf.address = x.target WHERE x.kind = 'call'; "; /// Transitive function-level reachability from the entry point over /// call/jump/branch edges. Useful for finding dead code /// (`SELECT address FROM functions /// WHERE address NOT IN (SELECT addr FROM v_reachability_from_entry)`) /// and for scoping analysis to the live subset. /// /// Seeds from the function containing the `entry_point` label and walks /// the recursive closure: a reachable function's instructions branch into /// the functions enclosing the branch targets, which are then reachable /// in turn. `UNION` (not `UNION ALL`) deduplicates to handle call-graph /// cycles (recursive functions, mutually-recursive pairs). const V_REACHABILITY_FROM_ENTRY: &str = " CREATE OR REPLACE VIEW v_reachability_from_entry AS WITH RECURSIVE reach(fn) AS ( SELECT i.function FROM instructions i JOIN labels l ON l.address = i.address WHERE l.name = 'entry_point' AND i.function IS NOT NULL UNION SELECT tgt.function FROM xrefs x JOIN instructions src ON src.address = x.source JOIN instructions tgt ON tgt.address = x.target JOIN reach r ON src.function = r.fn WHERE x.kind IN ('call', 'j', 'br', 'jt') AND tgt.function IS NOT NULL ) SELECT fn AS addr FROM reach; "; /// Reachability extended over `kind='ind_call'` edges from M5. Strict /// superset of `v_reachability_from_entry` — every fn there is also here, /// plus any function reached only via a vtable bcctrl whose vtable+slot /// the M5 dataflow could resolve. Sample 5 newly-reachable PCs in canary /// before trusting widely; the analysis intentionally leaves out alias- /// dependent indirect calls (vtable loaded from a `this` field). const V_INDIRECT_REACHABILITY_FROM_ENTRY: &str = " CREATE OR REPLACE VIEW v_indirect_reachability_from_entry AS WITH RECURSIVE reach(fn) AS ( SELECT i.function FROM instructions i JOIN labels l ON l.address = i.address WHERE l.name = 'entry_point' AND i.function IS NOT NULL UNION SELECT tgt.function FROM xrefs x JOIN instructions src ON src.address = x.source JOIN instructions tgt ON tgt.address = x.target JOIN reach r ON src.function = r.fn WHERE x.kind IN ('call', 'ind_call', 'j', 'br', 'jt') AND tgt.function IS NOT NULL ) SELECT fn AS addr FROM reach; "; /// Convenience join: each function's first decoded instruction. Useful for /// quickly inspecting prologue patterns without computing offsets manually. const V_FUNCTION_FIRST_INSTRUCTION: &str = " CREATE OR REPLACE VIEW v_function_first_instruction AS SELECT f.address AS function_addr, f.name AS function_name, i.raw AS first_raw, i.disasm AS first_disasm, i.ext_disasm AS first_ext_disasm FROM functions f JOIN instructions i ON i.address = f.address; "; /// Every call site of a kernel/library import, one row per branch. /// /// Driven by `instructions.import_address` — the foreign key onto `imports` /// laid down by [`crate::imports::ImportSites`] — rather than by matching /// `xrefs` against import-kind labels. That change fixes two things: /// /// - a tail-call `b` into a thunk is a call to that import, but `xrefs` tags /// it `'j'`, so the label-matching version silently dropped all of them /// (64 sites on the reference title, against 3,217 `bl`); /// - the library and ordinal are now available without re-parsing the name, /// so `WHERE library = 'xboxkrnl.exe'` works directly. /// /// `call_site` is the branch instruction; `import_addr` is the thunk head. const V_IMPORTS_CALLED: &str = " CREATE OR REPLACE VIEW v_imports_called AS SELECT i.address AS call_site, i.function AS function_addr, f.name AS function_name, i.mnemonic AS instruction, i.import_address AS import_addr, im.library AS library, im.name AS import_name, im.ordinal AS ordinal FROM instructions i JOIN imports im ON im.address = i.import_address LEFT JOIN functions f ON f.address = i.function WHERE i.import_role = 'call'; "; /// Every recovered `switch` case, joined to the dispatching function and to /// the label on the case body. One row per case *value* — several rows can /// share a `target_address` when case values fall through to one body. const V_SWITCH_CASES: &str = " CREATE OR REPLACE VIEW v_switch_cases AS SELECT jt.bctr_pc AS dispatch_pc, jt.function AS function_addr, f.name AS function_name, jt.kind AS table_kind, jt.table_address AS table_address, e.case_index AS case_index, e.target_address AS target_address, l.name AS target_label FROM jump_tables jt JOIN jump_table_entries e ON e.bctr_pc = jt.bctr_pc LEFT JOIN functions f ON f.address = jt.function LEFT JOIN labels l ON l.address = e.target_address; "; /// The C++ inheritance graph as recovered from RTTI. Index 0 of a base-class /// array is the class itself and is excluded, so every row is a genuine /// `derived -> base` edge carrying the displacement triple needed to find the /// base subobject inside an instance. const V_CLASS_HIERARCHY: &str = " CREATE OR REPLACE VIEW v_class_hierarchy AS SELECT DISTINCT dtd.demangled_name AS derived_class, b.name AS base_class, b.base_index AS base_index, b.mdisp AS mdisp, b.pdisp AS pdisp, b.vdisp AS vdisp, c.vtable_address AS derived_vtable FROM rtti_base_classes b JOIN rtti_locators c ON c.class_hierarchy = b.class_hierarchy JOIN rtti_type_descriptors dtd ON dtd.address = c.type_descriptor WHERE b.base_index > 0; "; /// Virtual methods per class, resolved through the RTTI-named vtable. The /// authoritative counterpart to querying `methods` by an `ANON_Class_*` name. const V_CLASS_METHODS: &str = " CREATE OR REPLACE VIEW v_class_methods AS SELECT td.demangled_name AS class_name, c.subobject_offset AS subobject_offset, v.address AS vtable_address, m.slot AS slot, m.function_address AS method_addr, f.name AS method_name, f.has_eh AS method_has_eh FROM rtti_locators c JOIN rtti_type_descriptors td ON td.address = c.type_descriptor JOIN vtables v ON v.address = c.vtable_address JOIN methods m ON m.vtable_address = v.address LEFT JOIN functions f ON f.address = m.function_address; "; /// Which function references which string literal. The single most useful /// orientation query in a stripped binary: it is how you find the code behind /// a message you can see on screen. const V_FUNCTION_STRINGS: &str = " CREATE OR REPLACE VIEW v_function_strings AS SELECT x.source_func AS function_addr, f.name AS function_name, x.source AS reference_pc, x.kind AS reference_kind, s.address AS string_addr, s.encoding AS encoding, s.content AS content FROM xrefs x JOIN strings s ON s.address = x.target LEFT JOIN functions f ON f.address = x.source_func WHERE x.kind IN ('ref', 'read'); ";