# Phase 2 results — symbol porting with measured confidence Tooling: `phase2_port_symbols.py` -> `phase2_port.json`. Tiers: **EXACT** (identical bytes) · **CREATED** (identical bytes occurring *uniquely*, function created because IDA had not recognised it) · **MNEM** (identical mnemonic sequence, operands differ) · **FUZZY** (mnemonic similarity >= 0.90) · **UNMATCHED** (deliberately left unnamed — never guessed). ## The validation gate: self-port must reach ~100% Porting the reference symbols onto the reference's *own* build: | family | self-port | verdict | |---|---|---| | HDCP 23.0.0.82 | **100.0%** (EXACT 101, CREATED 8, UNMATCHED 0) | PASS | | SYS_DRV 0.49.0.54 | **100.0%** (EXACT 1139, CREATED 2, UNMATCHED 0) | PASS | | ASD 33.0.1.21 | **100.0%** (EXACT 1548, CREATED 14, UNMATCHED 0) | PASS | | sOS 0.49.0.54 | 98.5% (UNMATCHED 8) | PASS, explained below | ### The first run FAILED this gate and was fixed, not accepted HDCP self-port initially scored **92.7%** — 8 reference functions unmatched in their own binary. Cause: auto-analysis recognises 102 functions where the reference has 109 (the gap flagged at the end of Phase 1). Rather than accept it, an **EXACT-CREATED** pass was added: if a reference function's bytes occur *uniquely* in the image, create the function there. That is a sound placement, not a guess, and it took the self-port to 100%. ### Why sOS stops at 98.5% — and why that is correct behaviour 34 sOS reference functions have **byte sequences that are not unique within their own image**. They are interchangeable micro-stubs: sos_nop_retarg / sos_nop_svc9A / sos_nop_hook1..3 / sos_nop_svcAE 2 bytes each exc_irq_hang / hang_0 / hang_1 4 bytes sos_stub_ret8_a/b/c, svc_8B_8F_7F_stub9, svc_94_95_84_stub9 4 bytes There is no content-based way to tell `sos_nop_retarg` from `sos_nop_svcAE` — they are identical bytes. The porter **refuses to place them** rather than assigning a name by position. That is the anti-corner-cutting rule working as intended, and these stubs are semantically irrelevant to diffing. ## Coverage achieved per family (named % of reference symbols) | family | oldest build | newest build | |---|---|---| | sOS | 92.0% (0.49.0.37) | 98.5% (0.49.0.54) | | SYS_DRV | 93.2% (0.49.0.37) | 100% (0.49.0.54) | | HDCP | 59.6% (23.0.0.49) | 100% (23.0.0.82) | | ASD | 47.5% (33.0.0.154) | 100% (33.0.1.21) | Compare with the **4.4-12.9%** coverage of the previous diffing effort (`PLAN-COMPLETE-VERSION-DIFF.md`). Even the worst build here (ASD 33.0.0.154 at 47.5%) is ~10x the old ASD coverage, and every modern build is above 85%. ## Honest reading of UNMATCHED `UNMATCHED` does **not** mean the tool failed. It means the function diverged far enough from the reference that no identity can be asserted at >=0.90 similarity. For diffing purposes that is itself a signal — those functions are *structurally changed*, and Phase 4 must report them as a separate class rather than silently dropping them from a name-keyed comparison. The ASD gradient is the clearest example: 820 unmatched at 33.0.0.154 falling monotonically to 0 at 33.0.1.21, with the module growing 176 KB -> 208 KB over that span. That is real churn, not matcher weakness. ## Note on the FUZZY tier FUZZY fires rarely (0-22 per build). The 0.90 threshold plus the +-25% size prefilter is strict by design; loosening it would trade correctness for coverage, which is the wrong trade for a diffing baseline. Leaving these in UNMATCHED is the safer outcome. ## Carry-forward into Phase 4 1. Diff only functions named at **EXACT / CREATED / MNEM** confidence in *both* builds being compared. 2. Report FUZZY-named functions separately (lower trust). 3. Report UNMATCHED as "structurally diverged" — a result, not a gap. 4. State per-pair how many functions were actually comparable. ================================================================================ # Phase 4 — gate result **The pipeline was required to re-discover the known fixes before any new result could be trusted. It did, and it refined them.** | target | found | where | |---|---|---| | `sos_check_va_window_request` | YES | ADDED in `sos 0.49.0.51 -> 0.49.0.53` | | `sysdrv_tee_map_shared_mem` | YES | ADDED in `sys_drv 0.49.0.51 -> 0.49.0.53` | | `range_overlap_check` | YES | same pair | | `ranges_must_not_overlap` | YES | same pair | ## And it corrected the existing documentation Both findings are titled "FIXED-IN-54". They actually landed in **0.49.0.53**: * the guard functions are absent from `.51`, present in `.53`; * **`.53` text is byte-identical to `.54`** for all four functions; * a **name-independent constant census** shows the sOS guard's window values (`0x3900000`/`0x3A00000`/`0x3800000`/`0x3700000`) occur **nowhere in the entire `.51` image**, but do in `.53` and `.54`; * `0.49.0.53` is a genuine distinct build (sha256 differs from both neighbours in both families). The original work compared `.51` against `.54` because **`.53` was not in the archive then** — it is still missing from `versions/*.bin`, present only as an ELF. Both FINDING docs have been annotated in place. ## Adjacent-pair change volumes (the new search surface) sOS 4, 12, 1, 1, 3, 5, 11, 3 changed per adjacent pair SYS_DRV 7, 38, 3, 16, 9, 7, 2, 12 changed per adjacent pair These are the semantic deltas now available for triage — computed over 483-1142 comparable functions per pair, against the previous effort's 4.4-12.9% coverage.