| Commit message (Collapse) | Author | Age |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
something
Finishes the rebuild that was interrupted earlier and leaves the corpus on a single decoder version
for the first time since the SVC-opcode work started. All 16 modules rebuilt clean, 16/16 self-port
gates PASS, no duplicate names in any of the 99 builds, no address-derived names.
The naming fix from the previous commit is now actually in the artifacts: 1,038 SVC stubs use the
libdump's sysdrv_rpc_<N> convention instead of my own rpc_<N>. Six names persisted in fw/cgnames from
when I wrote them by hand are converted too, otherwise they would have quietly reintroduced the
inconsistency on the next rebuild. The 85 remaining bare rpc_ names are of the form gcp_asd_rpc_202 --
ASD's libdump names ported into other modules through shared code, so that is the libdump's own
convention and correct to leave.
Effect on diffs, which is why this mattered: the 22.0.4.49 -> 22.0.4.50 pair reported 8 functions
added and 8 removed with no firmware change behind it, and now reports 4. The remaining 4 are a
different and smaller problem, now documented in ta_diff.py: a function can carry a richer name in one
build than another, gcp_cmdFE0C against gcp_cmdFE0C_CreateCryptoSession, because the semantic half
comes from a pass that only reaches some builds.
Corpus-wide the diffs are now usable. Adjacent patch versions average 0.5 changed functions in ras,
2.5 in wspe, 3.3 in merit, 4.6 in opm, up to 26 in xgmi where the builds are genuinely far apart.
Added/removed should be read as "look at this" rather than ground truth; changed-function counts are
sound.
Metrics unchanged by the rebuild at 19,487 meaningful names (67.3%) and 75.8% field share, which is
the point -- it confirms the mixed-decoder tree was not inflating anything.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Earlier in this work I recorded that the SVC 0xF2 stubs "carry no semantic information whatsoever",
since the operation is chosen by caller-set registers. That holds for the shared thunks and I
over-generalised it: in the per-module stubs the opcode is loaded by the stub itself.
DTM's three unnamed functions all decompile to the same four lines, "SVC 0xF2; return 2". The bytes
say otherwise. MOV.W R0,#0x1C0, MOVW R0,#0x101 and MOV.W R0,#0x100, each stored as the first word of
the request frame before R0 is set to the selector and R1 pointed at the frame. So the first stored
word is the opcode, and 0x100 independently matches a comment already in this tree reading
"Opcode 0x100 - DTM TOPOLOGY_UPDATE", which is the corroboration that makes this more than a plausible
decode.
Named <ta>_rpc_<hex>, following the existing asd_rpc_NNN convention, and only where exactly one
in-range immediate survives -- otherwise the stub stays unnamed rather than guessed.
Also named by reading the bodies: oc_memcmp_ct, whose loop accumulates "v3 |= *a1++ ^ *a2++" over a
counted range with no early exit and is therefore a constant-time comparison worth labelling;
csu_rpc_309 and csu_rpc_305, the same stub shape with the opcode differing across builds; and
auc_sysdrv_rpc_101C_fwd, an 8-byte forwarder that adds a2+80 as a fourth argument.
csu, oc, tmz and dtm are now 100% named. Corpus 19,126 -> 19,134 meaningful names.
What remains in the small modules is an AUC body of several hundred lines, a RAP command handler and
five FP functions. That is per-function reverse engineering rather than pattern work, which is the
honest characterisation of the rest of the corpus too.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
canonical entry points
Last commit asserted the log-string lever was GCP-only. That claim rested entirely on the AMD?TA
regex, so I re-checked it by dumping every message-like string from all 16 module binaries. POWERPLAY
logs "CreateEntry: status = 0x%x" and "InvokeCommand: status = 0x%x" -- a second form with no AMD_TA
prefix, naming the function BEFORE the colon rather than after it. The harvester now reads both.
POWERPLAY yields 10 agreements and 0 contradictions, independently confirming its entry points.
That is a small return, but it is the second time re-checking a ceiling claim found the claim wrong,
and the check is cheap compared with the mislabels it prevents.
POWERPLAY also showed that a log name is not automatically the best name. Build 34.0.0.9 had
0x402570 unnamed and its log calls it CreateEntry, while the other five builds name the same address
TA_CreateEntryPoint. Applying the log spelling verbatim named one function two different things
across builds and reduced consistency rather than improving it. Short forms of the five standard
entry points are now aliased to the canonical spelling, so build 34.0.0.9 gets TA_CreateEntryPoint
and TA_InvokeCommandEntryPoint, matching its siblings.
Confirmed in the artifacts: 0x402570 and 0x4026cc in powerplay_34.0.0.9 now read
TA_CreateEntryPoint and TA_InvokeCommandEntryPoint.
XGMI, OPM, MERIT and FP were checked under the bare form too and yield nothing -- their strings are
prose ("GetInfo for NodeId %d") or embedded certificates, not function names.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
my mislabels
I had claimed the unnamed residue was thunks and that no naming lever remained. Checking that claim
against the artifacts instead of repeating it showed only 43% of ASD's unnamed functions are
4-byte thunks, and that GCP's binary contains strings like
AMD_TA_GCP: AllocateDRMSession ENTER
AMD_TA_GCP: GetCryptoSessionForDevice
The leading identifier is the vendor naming the enclosing function. That outranks everything else
here: porting infers from byte similarity, the call graph from position, the dispatcher from a jump
table, but a string inside the body is the author's own label.
fw/ta_logname.py harvests them from the binary, resolves the referencing function through IDA data
xrefs, and applies the name. On GCP's 11 builds: 43 agreements with names already derived, 70
functions named that were unnamed, 193 instances contradicting an existing name.
The agreements are as valuable as the corrections. gcp_cmd01_AllocateDRMSession, derived from the
dispatcher table, is confirmed verbatim by the firmware's log line -- the first independent check
that dispatcher-anchored naming is sound.
Two mislabels of mine, both now fixed:
0x403a90 was cmdFE0F_GetCryptoSessionByDecodeHandle; its log says GetCryptoSessionForDevice
0x4038d8 was cmdFE0A_QueryAccessibilityAttributes__h9; its log says GetCryptoSessionByDecodeHandle
So the name was on the wrong function, and the function that really is GetCryptoSessionByDecodeHandle
had been filed as a fragment of a different command. cmdFE0A is QueryAuthenticatedSession, not
QueryAccessibilityAttributes, and nine __hN "fragments" are separate routines with their own names.
The wrong FE0F name was seeded from fw/libdumps/gcp.json, so it is corrected through LIB_RENAME,
which since the seed-consistency fix rewrites the seed as well as the databases.
A command id is carried onto the vendor name only when the old name had no __hN suffix: that suffix
means the id was inherited from a neighbour and never established for the address, so carrying it
would manufacture a false dispatcher mapping.
Ceiling: only GCP (72 distinct) and WSPE (7) carry these strings; the other 14 modules have none,
established by scanning every build binary. ASD has 41 printable runs, all property keys. Named
19,352 -> 19,422.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Three correctness audits this pass, and the third produced the work.
Duplicate function names: zero across all 99 builds. The fp_cmd04 duplicate fixed last pass was
the only one.
Contradictory names within a module: zero. 3566 byte-identical bodies were grouped per module and
every group agrees on its name, so the porting is self-consistent.
Contradictory names ACROSS modules: 32 identical bodies carry different base names, and that is
where the value was. Most are not contradictions at all but the same function known two ways --
the TA side knows it only by content hash (lib_52b900d8_88) while ASD's symbol set knows it by
name (memcmp_be). Byte identity makes the equivalence a fact rather than an inference, so the
placeholder loses: 21 clusters resolve to a real name, including memcpy_aligned, memcmp_be,
aeabi_uidiv, udiv10, and the vsnprintf_ family (setup, fill, pad_left, out_str, fmt_hex,
fmt_octal). 509 instances across 51 builds, merged into fw/cgnames so they are durable.
Names still carrying a content-hash placeholder are reported in the commit body rather than left
unstated, and the SHARED_ARG1 fragment list keeps BOTH the old hash fragments and the semantic
ones, because the upgrade is applied through the name map and the tee_fmt_state typing must keep
matching whichever name a given build ends up with.
Eleven clusters were deliberately NOT upgraded because the two names genuinely contradict rather
than one being a placeholder: gp_cipher_do_final against sysdrv_rpc_10, gp_cipher_update against
sysdrv_rpc_7, entry_trampoline against ta_main_loop. One of each pair is wrong and byte identity
cannot say which, so both are left standing rather than picking the one that reads better.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
DISPATCH names
An earlier pass reported that all 99 artifacts regenerate byte-identically. That was true of that
one run and does not hold in general. A full clean regeneration of all sixteen modules changes 220
files, and the cause is IDA's auto-analysis not being identical across fresh databases: AUC's
function count comes back 39 instead of 40, and decompiled text varies cosmetically
("(int)nullsub_3" against "nullsub_3"). The claim is corrected here rather than left standing.
Semantically the drift is small -- 4 names of 19,353 -- but all four were tier DISPATCH, which
exposed a wrong decision. fw/ta_snapshot_names.py excluded DISPATCH on the grounds that
fw/ta_re.py re-derives it every run. It does, but not deterministically: dispatcher recovery
depends on which function Hex-Rays decompiles cleanly on a given analysis, so xgmi_cmd00,
xgmi_cmd04, xgmi_cmd0B and fp_cmd04 simply did not come back. DISPATCH is now persisted, and the
exclusion reasoning in that file is rewritten to say why.
Persisting it still cannot mask a regression: the dispatcher step runs regardless, so where
recovery works the DISPATCH tier wins and where it fails the restored name appears as tier IDA.
The tier difference is itself the signal that recovery failed on that build.
7817 names were recovered from the committed artifacts into fw/cgnames so nothing derived is left
depending on a database that may not reproduce. XGMI came back whole.
The one name that did NOT come back turned out to be a defect being fixed rather than lost. HEAD
had TWO functions called fp_cmd04 -- 0x401334 from the dispatcher and 0x402b4c from byte porting
-- and the current dispatcher map routes command 4 to 0x402b4c, agreeing with the ported name. So
HEAD carried a duplicate, this run does not, and the allow_overwrite=False guard on the re-apply
is what refused to restore the bad one. Worth keeping: a name count going DOWN was the correct
outcome, and reading it as a regression would have reintroduced the duplicate.
Verified state after the clean regeneration: 16/16 gates PASS, 99 builds, 19,349 of 28,945 named
(66.8%), field share 66.4%, 21 decompile failures.
|
|
|
files
This pass fixes a fragility that was actively destroying work rather than adding coverage.
Four IDA databases have now come back corrupted with "worker exited with status 1", and the only
remedy is to delete and rebuild them. Every name derived by fw/ta_cgname.py lived solely in those
databases, so a rebuild discarded them -- GCP silently fell from 83% to 74% named when I wiped its
databases for the dispatcher fix, and I only noticed because I happened to re-measure per module.
Worse, those names cannot be re-derived: once a callee has a name it stops being a sub_* candidate,
so re-running the tool on an already-processed build reports nothing to do.
Three changes:
* fw/ta_cgname.py persists every name it applies to fw/cgnames/<ta>.json, keyed by build and
address.
* fw/ta_re.py re-applies that map at the start of each build, before anything reads names, so a
rebuilt database comes back with its derived names intact.
* fw/ta_snapshot_names.py recovers the names applied before the map existed. They are read out
of fw/decomp/*.index.json, which records name, address and tier per function, and only the
IDA tier is snapshotted. EXACT, MNEM, FUZZY and DISPATCH are deliberately excluded because
fw/ta_re.py re-derives them every run; persisting those would bake a derived result into the
repository and hide a future regression in the porting itself.
1476 names are now durable across 15 modules, including ASD's 847, which would otherwise have
needed a 40-minute wipe-and-rederive to recover.
Verified by doing the thing that caused the loss: XGMI's databases were deleted and rebuilt. It
came back at 238 of 334 named against 239 before, so a full database loss now costs one function
instead of nine percentage points. The single remaining loss is not chased down here, but it is
recorded rather than rounded away.
GCP is restored to 84% and HDCP re-derived cleanly to 86% with a complete map. Corpus naming
stands at 67.0%.
|