Files
Sylpheed/crates/sylpheed-xexdb/src/sql_views.rs
MechaCat02 9c48c340bd fix(xexdb): clear the lint gate on the imported crates
rustfmt, then clippy -D warnings across the three new crates. Mechanical,
except three decisions that are stated rather than silently allowed:

  * lzx.rs gets file-scoped needless_range_loop/explicit_counter_loop allows.
    Index arithmetic IS the algorithm -- LZX is defined over symbol indices,
    Huffman slots and window positions, and a decompressor that is merely
    idiomatic is worth nothing if it is not bit-exact.
  * sylpheed-xexdb gets crate-scoped allows for needless_range_loop (nine
    sites index reg[r] where r is the PowerPC register number -- the index is
    the meaning), too_many_arguments and type_complexity. This code arrived
    whole from a retired repository; a refactor here would be an unreviewed
    edit dressed as a lint fix.
  * Everything else clippy asked for is FIXED, including all 14 doc-indent
    sites, the let-else, and a Prepared type alias in the binary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 20:25:44 +02:00

288 lines
11 KiB
Rust

//! 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;
";
/// Per-function summary of which kernel/library imports it calls. Joins
/// xrefs (call edges) against the labels table to surface import names.
const V_IMPORTS_CALLED: &str = "
CREATE OR REPLACE VIEW v_imports_called AS
SELECT
x.source_func AS function_addr,
f.name AS function_name,
x.target AS import_addr,
l.name AS import_name
FROM xrefs x
JOIN labels l ON l.address = x.target
LEFT JOIN functions f ON f.address = x.source_func
WHERE x.kind = 'call'
AND l.kind = 'import';
";
/// 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');
";