| 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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
known names
The previous pass left 102 stubs unnamed because they load more than one in-range constant. The
tempting fix was to take the first immediate, which matches 4 of 4 multi-immediate stubs that carry an
established name -- and that number is worthless, because all four are the same XGMI function in four
builds. It is n=1 dressed up as n=4, and applying it to 102 functions would have been a guess with a
validation figure attached.
The structural rule earns the names instead: track MOV/MOVW/MOVS immediates per register, then return
whichever register is stored to [SP,#0], which is the first word of the frame R1 is pointed at before
the supervisor call. Against the 940 stubs the libdump already names *_rpc_N it matches 940 and
disagrees on 0, with no abstentions, and it resolves all 102 stubs the count heuristic could not.
So the weaker rule is superseded rather than patched, and the audit records both along with why the
4/4 figure was rejected -- that reasoning is the part worth keeping.
Regenerated gcp, hdcp, merit, opm and asd. Corpus 19,405 -> 19,433 meaningful names (67.1%), no
duplicate names in any of the 99 builds, address-derived still 0.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
corpus to 67.0%
The opcode decode that closed dtm and csu last commit is not module-specific, so I measured its reach
and validated it before applying it anywhere.
Validation came free: the libdump already names 299 single-immediate SVC stubs *_rpc_N, which is
independent ground truth for exactly this decode. The decoded immediate matches the established
number in 296 of 299. The three exceptions are one XGMI function across three builds whose libdump
name uses a different numbering scheme, not a bad decode.
Applied as a pipeline pass, keyed on bytes rather than addresses so it survives database rebuilds,
and only where exactly one in-range immediate survives -- 102 ambiguous stubs stay unnamed rather
than guessed. 501 stubs named: hdcp 87 -> 96, fp 92 -> 95, gcp 87 -> 91, opm 62 -> 75, merit 76 -> 79,
xgmi 75 -> 76, ras 35 -> 38, asd 64 -> 65, powerplay and wspe smaller. Corpus 19,134 -> 19,405
meaningful names, 66.1% -> 67.0%.
A second corroboration turned up after the fact: HDCP's newly decoded hdcp_rpc_161 carries the same
service number as ASD's independently libdump-named asd_rpc_161, so two modules calling the same
SYS_DRV entry agree without either having been told about the other.
Names are uniquified per build; no build contains a duplicate name, checked across all 99.
Worth noting what made this possible: the claim being overturned was my own, that these stubs "carry
no semantic information whatsoever". It was right about the shared thunks and wrong about the
per-module ones, and I had written it down as settled.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
ta_entry_points read the trampoline's +0xA0 (Create) and +0xB0 (Invoke) slots and threw away the
three between them. Those are TA_DestroyEntryPoint, TA_OpenSessionEntryPoint and
TA_CloseSessionEntryPoint. That is the whole reason TMZ sat at 50% named: it has six functions and
three of the unnamed ones were entry stubs the header identifies exactly.
The slot order is read off GCP rather than assumed from the GP spec. GCP has all five named
independently and its table reads Create, nullsub(destroy), OpenSession, CloseSession, Invoke, with
WSPE and HDCP agreeing and the types agreeing too -- slots 0, 2, 4 return a status, slots 1 and 3 are
void. Running the new derivation against GCP reproduces GCP's own names exactly, which is the check
that makes this evidence rather than convention.
Applied to every module, only where porting left the address unnamed. Per-module: tmz 50 -> 100,
rap 76 -> 98, csu 70 -> 92, oc 90 -> 97, dtm 75 -> 89. Corpus 19,038 -> 19,126 meaningful names
(65.8% -> 66.1%), field share 75.5% -> 75.8%.
RAS disagrees and is deliberately left alone. Its libdump names TA_CreateEntryPoint and
TA_DestroyEntryPoint at addresses the header assigns to OpenSession and CloseSession, a systematic
two-slot shift across all 18 builds. My first run wrote the header names anyway, which left RAS
asserting two different Create entry points -- a contradiction I introduced and then caught by
checking for duplicated roles. A guard now refuses to add a second holder of a role, so the conflict
is reported on every build and nothing is written. No build has a duplicated entry-point role.
Which source is right for RAS stays open: the obvious tiebreaker, argument count, is not decisive on
functions IDA never prototyped.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
to 1
Two errors of mine found by measuring the typing residue per build instead of summed across builds.
First, the per-function table in AUDIT-TA-STRUCTS.md summed each function's dereferences over all
builds, which made the residue look concentrated -- asd_link_sub_C930 appeared to hold 158 when it
holds 14 in any single build. Measured per (build, function) pair, which is what a reader actually
opens, the distribution is flat: median 2, mean 2.7, p90 4, max 33 across 1,360 pairs. The max is
GCP's start, the entry stub, where raw pointer arithmetic is correct and typing would be wrong.
Second, that measurement surfaced one real target, and it had been excluded for a wrong reason. The
recorded note said its dereferences "go through a local (v10), not a1, so typing a1 would not reach
them". The body says "v10 = a1". The note was wrong, and because it read as a settled finding it was
never rechecked -- the same failure mode as the thunks-only and GCP-only claims.
The real disqualifier was the documented padding rule: offsets 0x1-0x3, 0xD, 0xF9, 0x106 and 0x250
landed inside declared pad arrays. Per union-never-intersection those offsets belong in the layout,
so pad00, pad08, pad55 and pad24E are split to name them. Struct size is unchanged at 0x26C and all
ten observed offsets now resolve to named fields, verified by walking the declaration offset by
offset before applying it.
In the artifacts, across all 7 HDCP builds: 24 raw dereferences per build to 1, with 22 typed field
accesses. Corpus named-only field share 74.5% to 75.6%, raw dereferences 3,661 to 3,506. Named count
unchanged, and HDCP's gate still passes.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
overflow site
hdcp1_first_part_authentication carries the HDCP overflow that AUDIT-TA-MEMSAFETY-HUNT records
as confirmed on hardware at 0x402cde, with the copy length taken from a byte struct field. It
was still "int a1" with eleven vN locals.
hdcp1_auth_req is derived from the accesses across all seven builds: a dword at 0x00, a dword at
0x04, bytes 0x08..0x0E and halfwords at 0x10 and 0x12, closing at 0x14. The block is what the
invoke entry passes as v7 + 12, i.e. hdcp_req.argA, which is consistent with the request layout
already derived.
The first dword is named session_idx on direct evidence rather than by analogy: the handler reads
it, bounds it with "<= 7", and then indexes a stride-652 table as
"dword_40237C + 652 * session_idx". Eight sessions of 652 bytes.
Typing a2 was already in place from the earlier argN work; a1 now joins it, and
hdcp1_first_part_authentication's locals session_idx and status are named.
Recorded limitation rather than smoothed over: the documented bug site loads its length from
[r5, #0x38], and r5 is NOT v12 in this build -- register allocation differs between the build the
memsafety audit used and the reference here. The local names are therefore keyed on what each
local demonstrably does in this build, and the two that do not correspond are left alone rather
than mapped by assumption.
Also honest about the measured effect: HDCP's field share moves only 70.0% to 70.2%, because
Hex-Rays keeps the explicit "*(_DWORD *)a1" cast at several of these sites even with the pointer
typed, so the signature improves while those particular lines do not. The win here is the
signature and the session_idx/652-stride relationship being legible, not the metric.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Two GCP handlers were mislabelled in the libdump symbol set and the mislabels had been
propagating into every artifact. The duplicate name was the tell: FE0B and FE0D were both called
DestroyAuthenticatedSession.
The code settles it without needing the documents. gcp_cmdFE0C iterates ctx->crypto, zero-fills a
0x58 gcp_crypto_session and sets crypto->state and crypto->key, so it creates a CRYPTO session.
gcp_cmdFE0D computes "(char *)ctx + 88 * HIBYTE(arg10)" -- stride 88, the crypto array at
ctx+0x800 -- while gcp_cmdFE0B uses stride 80, the auth array at ctx+0x300. AUDIT-NEW-TAS-RE.md
states exactly that split, and the hardware findings agree: FINDING-GCP-ACQUIRE-GATE-BYPASS
records 0xFE0C as CreateCryptoSession.
Worth distinguishing from the FE07 case: that one claimed the wrong COMMAND and the dispatcher
cross-check caught it automatically. These two claim the right command and the wrong SEMANTICS,
so no command-id check can see them -- only reading what the code touches. The corrections are
applied by name through a RENAME_FIX table so they hold for every build.
While verifying them, the dispatcher kept printing the old callee name in four builds after the
rename had demonstrably succeeded. Hex-Rays caches pseudocode per function and
remote_ops.rename refreshes only the function it renamed, never its callers, so a stale cache was
reading as a failed rename. Every function's cached text is now invalidated before the artifacts
are emitted.
That turned out to matter far beyond the two names. Applied TYPES were also being hidden behind
stale text: GCP's field share goes 59% to 83.4%, its typed field accesses 1716 to 3081 and its raw
dereferences 1193 to 612, with no new structure added -- the types had been applied all along and
the emitted artifacts were showing pre-typing output. WSPE lands at 91%, HDCP 70%, XGMI 69%,
POWERPLAY 67%. Corpus field share 65.9% -> 66.4%, held down by ASD, which dominates the corpus and
barely moved.
This is the sixth consecutive pass whose main finding was that something already done was not
visible in the output. The remaining low scores are now informative rather than suspect: merit at
0% and opm at 19% are low because their structures are mostly opaque body arrays, not because
typing failed.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
reach
Two of the three largest remaining dereference groups in named functions were not blocked by
analysis at all: hdcp2_process_auth_message_v2 with 161 and hdcp1_create_session with 49 both take
their structure as the SECOND argument, and every typing path built so far rewrites only the first
parameter. retype_arg1 is generalised to retype_arg with a 1-based index, with the old name kept as
an alias so the existing call sites are untouched, and a per-module ARGN_BY_TA table drives it.
hdcp2_auth_msg is contiguous dwords 0x04..0x24 followed by an 8-byte read at 0x28, which is
8-aligned, so it closes at 0x30.
hdcp1_session_req is sparser, and its 8-byte read at 0x04 is NOT 8-aligned. It is declared as two
dwords rather than a 64-bit field: declaring the wide read as one member would force alignment
padding and shift every later offset, which is the same trap asd_link_cfg's unaligned 0x14 qword
read presented. Size verified at 0x20.
HDCP field share 62.8% -> 68.9%, corpus 65.2% -> 65.9%, raw dereferences 7192 -> 6982.
Arg-based typing in named functions is now effectively exhausted, and the numbers say so rather
than my asserting it: the total has gone 2201 -> 1507 -> 1163 across three passes, and every group
still standing touches one or two offsets. A structure for a function that reads two offsets is a
one-field structure, which buys nothing a raw offset did not already show, so the remaining
dereferences are deliberately left alone rather than dressed up.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Re-running the concentration analysis after last pass's fix left
hdcp2_rx_RepeaterAuth_ReceiverID_List at 135 raw dereferences while its signature plainly showed
"struct hdcp2_session *a1". Both facts were true: it is typed in 4 of 7 builds and unnamed in the
other 3, and the name-keyed rule cannot reach a function it cannot find by name.
A signature rule for exactly this case was measured when hdcp2_session was first derived -- 3
unnamed functions, 135 dereferences, zero false positives among the named -- and then never
implemented. It is now wired in through a generic SIG_BY_TA table rather than another
module-specific branch, since ASD already had its own copy of this idea.
The predicate requires every a1 offset to fall inside the structure's declared FIELDS rather than
anywhere within its extent. Accesses landing in padding are treated as disqualifying, because
padding means the layout does not describe that function -- which is the check that would have
caught the hdcp2_tx_RepeaterAuth_Stream_Manage mistype earlier without needing to be reasoned
about by hand.
Checking that condition also found two offsets, 0x222 and 0x224, that real accesses touch while
hdcp2_session had them as padding. Both are now fields; 0x224 is a single byte rather than a
halfword because 0x225 begins the next run, and the size is re-verified at 0x26C.
Two process notes worth keeping. The insertion failed silently the first time -- the anchor text
did not match, the assertion fired inside a heredoc whose output was swallowed by the following
command, and the pipeline then ran happily without the new rule. It was caught only by grepping
for SIG_BY_TA in the generated file rather than trusting that the edit had landed, which is the
same class of silent no-op as the last four passes.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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%.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Rather than chase another percentage, this pass audited whether the names already applied are
CORRECT, using the only authority available: the dispatcher. For every build, each handler whose
name claims a command id was checked against the command the dispatcher actually routes to it.
257 handlers checked, 246 agreed, and 11 conflicted -- all in GCP, all of the form
gcp_cmdFE0A_QueryAccessibilityAttributes__hN being routed from commands 0x8, 0x9, 0xA and 0xF.
Those are not GCP commands at all. They are arms of 0xFE0A's own sub-command switch, absorbed as
top-level commands because dispatch_map merged EVERY switch-bearing callee at the shallowest
depth. The handler names were right; my command map was wrong, which is the opposite of what the
conflict report implied.
The merge is replaced by taking only the richest switch at the shallowest depth. The union was
introduced to allow a TA that splits low ids and 0xFExx ids across sibling switches, but GCP
serves both from one function, so nothing is lost: the reference still recovers 14 commands, all
14 still match fw/cmdmaps/gcp_ea.json address for address, and the phantom 0x8/0x9/0xA/0xF are
gone. The three still missing (0x3, 0xFE08, 0xFE0E) remain the inlined arms with no callee, as
before.
Re-running the audit afterwards: 332 handlers checked, 332 agree, zero conflicts. The FE07 case
that had been reported since the cross-check was built is also clear, because the pipeline now
relabels that handler from gcp_cmdFE03_* to gcp_cmdFE07_* rather than leaving the inherited
misnomer in place.
Two more corrupted databases were found and rebuilt (hdcp_23.0.8.49, merit_24.0.4.42), with the
same "worker exited with status 1" symptom as the gcp one last pass. Rebuilding loses the
call-graph names on those builds, so ta_cgname was re-run for both; naming lands at 66.5% rather
than 66.9% because of that churn.
Also measured and rejected this pass, so they are not retried: cross-module naming by exact body
hash yields 77 names, and by mnemonic sequence 170 (against 1496 that match an ambiguous cluster
and cannot be resolved). Together under 2% of what remains unnamed, which is not worth the
regeneration cost.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The tool built last pass was only ever run on asd and ras. It is generic, so it is now run on the
rest, and the mismatch-rate control holds everywhere -- 0 to 4 mismatched anchors against 16 to 83
aligned, which is why the results are applied rather than argued about.
Yields are very uneven and that is informative in itself:
hdcp 150 names 65% -> 86% named
gcp 18 + a partial earlier run 74% -> 83%
merit 20 69% -> 74%
opm 13 58% -> 60%
xgmi 13 67% -> 72%
powerplay 5 68% -> 69%
wspe 4 82% -> 83%
HDCP and GCP pay because their corpus builds are close to their reference; powerplay and wspe
were already well covered by byte porting, so there was little left for the call graph to add.
Corpus overall: 19,377 of 28,947 named, 66.9%, up from 65.8% and from 35.4% when this work
started. Tiers now 7813 THUNK, 7503 EXACT, 2261 MNEM, 1424 IDA, 194 DISPATCH, 182 FUZZY.
One operational note worth recording: the gcp run aborted partway with "Failed to open database
gcp_22.0.0.71_a4b4fff1.elf.i64". Builds processed before the abort had already been saved, so the
retry found them named and reported only the remainder -- the 18 figure above understates what gcp
actually gained. Clearing the unreadable database and re-running fw/ta_re.py rebuilt it; nothing
was lost, but a partial-save interruption is a state this flow does not detect on its own.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
truncation
Two parts, both driven by the same recurring failure: a layout derived from a subset of its users
comes out wrong.
First, a truncation audit of all 24 declared structures, since asd_link_cfg and then hdcp2_session
had both been understated. For each module the maximum offset still reached by a raw argument
dereference across every build is compared against the declared size. Nothing is over its bound --
OPM's 0x341C sits inside its 0x3D48, HDCP's 0x264 inside 0x26C -- so the request structures hold.
Two apparent anomalies are artefacts of the audit script rather than defects: gcp_ctx and
merit_table compute as 0xC and 0x0 because the size walker does not account for nested structure
arrays.
Second, hdcp2_session needed revising for the third instance of the subset problem. Its first
version came from the three heaviest rx handlers. Adding the transmit-side handlers to the union
exposed two errors in it: 0x2C is read as a DWORD where the rx-only union had left byte padding,
and 0x246..0x24C is a byte run that had fallen inside pad235. Both are now fields, and field18
widens to eight dwords covering 0x18..0x34. The merge also had to resolve a genuine overlap --
0x2D is read as a byte inside the dword at 0x2C -- by giving dword coverage precedence, since a
narrow access into a wider field is ordinary rather than evidence of a separate member.
Three transmit-side handlers are now typed by name as well: hdcp2_tx_AKE_Init,
hdcp2_tx_SKE_Send_Eks and hdcp2_rx_AKE_Send_H_prime, whose offsets are covered by construction
because they contributed to the union. hdcp2_tx_RepeaterAuth_Stream_Manage is deliberately left
out despite holding 164 dereferences: they go through a local, v10, not a1, so typing a1 would not
reach them and claiming otherwise would be wrong.
A signature-based rule for HDCP was also built and measured before being kept small: it selects 3
unnamed functions holding 135 dereferences with zero false positives among the named ones, which is
honest but minor next to the by-name typing.
Measured across fw/decomp: HDCP went from 53 typed field accesses against 1881 raw dereferences to
1365 against 1039. Overall typed field accesses 11436 -> 11971, raw 8733 -> 8481, field share
56.7% -> 58.5%, struct-typed signatures 6.4%.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
An earlier pass examined HDCP's three heaviest handlers -- hdcp2_rx_AKE_Send_Pairing_Info,
hdcp2_rx_RepeaterAuth_ReceiverID_List and hdcp2_rx_AKE_Send_Cert, 590 dereferences between them
-- took their INTERSECTION, found only two offsets in common, and recorded them as three
unrelated message layouts not worth a shared structure.
That was the wrong test, and it is the same mistake that made asd_link_cfg come out at half its
real size. Handlers over a shared context each touch a different subset of fields, so the
intersection shrinks as handlers are added while the union is what actually describes the
structure. They do share 0x04, 0x10, 0x18 and 0x24, and two of them share the byte run at
0x107..0x10E, which the intersection of all three hid.
The union across all builds gives 56 observed offsets from 0x04 to 0x268, with each field's width
taken from the accesses themselves rather than assumed: dword runs at 0x18..0x28, 0x30..0x34,
0x3C..0x48 and 0x254..0x268, byte runs at 0x4F..0x54, 0x107..0x10E and 0x225..0x234, halfwords at
0x192..0x198, and single flag bytes at 0x2D, 0x38 and 0x24D. Unobserved spans are declared as
padding rather than invented as fields, and the declaration is generated programmatically so the
run arithmetic closes at 0x26C by construction instead of by hand.
CAVEAT recorded in the declaration: 0x26C is the extent observed, not a bound the firmware
checks, and the padding runs are genuinely unknown rather than known-unused -- precisely the
situation that understated asd_link_cfg, so a richer build may extend this too.
Two pieces of plumbing were ASD-specific and are now generic, since HDCP is the second module to
need them: load_type_decls already read <TA>_INTERNAL_DECLS, so the structure is declared under
that name, and the name-keyed arg1 map is now looked up per module through ARG1_BY_TA instead of
being hardcoded to ASD. The gate for that block silently failed to be rewritten on the first
attempt and was left as 'if ta == "asd"', which is why the typing did not fire until it was
checked rather than assumed.
Applied, the handlers read a1->field225, a1->field18, a1->field107 and so on, 3 of 3 functions on
most builds.
Measured across fw/decomp: typed field accesses 10789 -> 11436, raw pointer-arithmetic
dereferences 9323 -> 8733, field share 53.6% -> 56.7%, struct-typed signatures 6.0% -> 6.2%.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Rather than pick the next target by hand, the artifacts were measured for where raw dereferences
through function ARGUMENTS concentrate. That surfaced something better than another per-module
struct: the same offset set (0x08, 0x18, 0x1C, 0x20) appears through a1 in gcp, powerplay, wspe
and xgmi, in functions whose names are content-hash clusters -- lib_e53fbaaa_32, lib_7fca7c43_82,
lib_8680f995_112, lib_366a1ceb_178.
RAS is what identifies it. There the same cluster carries real names -- vsnprintf_fmt_int,
vsnprintf_fmt_ll, vsnprintf_pad, vsnprintf_prefix -- and adds 0x10, 0x14 and the tail 0x24 byte,
0x25 byte, 0x26 word, closing the layout at 0x28. It is the runtime's string-formatting state.
Declaring ONE shared type here is sound where the powerplay ack-word guess was not, and the
difference is worth stating: those "_<hash8>_" names come from ta_libmatch.py, which accepts a
cluster only when the function bytes match EXACTLY across modules. The functions are literally
the same code, so they necessarily share the structure. The earlier ack-word generalisation
rested on a resemblance and was wrong.
Applied via the first-parameter rewrite, so each function keeps the rest of its recorded
signature: 24 functions in RAS, 6 each in wspe and xgmi, 4 each in gcp and powerplay.
HDCP was examined and deliberately NOT given a shared struct. Its three heaviest functions --
hdcp2_rx_AKE_Send_Pairing_Info, hdcp2_rx_RepeaterAuth_ReceiverID_List and hdcp2_rx_AKE_Send_Cert,
590 dereferences between them -- share only two offsets, 0x18 and 0x24. They are distinct HDCP2
message layouts, not one structure, and forcing a common type would have misdescribed all three.
Honest limit on the reach of this change: the cluster is only NAMED in five modules. In hdcp, opm,
merit, auc, fp, rap, csu, oc, dtm and asd the same helpers exist but are still sub_*, so the
fragment-keyed typing cannot attach to them. Running the libnames clustering across those modules
first would let the same single declaration cover them too -- the clearest next step.
Measured across fw/decomp: typed field accesses 5246 -> 7101, raw pointer-arithmetic dereferences
12701 -> 11849, field share 29.2% -> 37.5%, struct-typed signatures 4.8% -> 5.7%.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
HDCP was the worst remaining module by readability: 1742 raw pointer-arithmetic dereferences,
zero typed field accesses, zero struct-typed signatures.
Its layout comes from TA_InvokeCommandEntryPoint, which takes the host buffer as v7 = *params,
switches on *(_DWORD *)v7 across 19 cases, and hands handlers two interior blocks at v7 + 12
and v7 + 5136. The size floor is the dispatch guard itself -- it proceeds only when
params[1] > 0x2817 -- and both blocks close exactly against it: 0x0C + 0x1404 = 0x1410 and
0x1410 + 0x1408 = 0x2818.
A recorded comment is wrong about that guard and is now flagged in the declaration, because the
comment-application added earlier propagates it into the artifacts: it claims the entry
"validates that params[0].size <= 0x2817", while the code dispatches when the size EXCEEDS
0x2817. It is a floor, not a ceiling.
Three fixes to the locator, each found by its own failure rather than by inspection:
* it was reading the switch-bearing function, but 'deref' describes what the ENTRY does with
the params pointer, and HDCP's switch is not in the function that does 'v5 = *a4'; deref
mode now always reads the entry;
* the candidate argument name was hardcoded, so builds where no prototype has been applied
call it a4 rather than params and matched nothing. Widening it to every parameter was
worse -- it matched an unrelated assignment and typed the wrong local on the reference
build -- so only the declared name and the last parameter are tried;
* an applied prototype can introduce a cast, so an optional cast is now allowed before the
dereference.
Applied, the entry reads v7->cmd_id, v7->argA and v7->argB across 25 accesses. It lands on 2 of
7 HDCP builds; the other five do not expose the assignment in a shape the locator recognises and
are left untyped rather than forced.
Measured across fw/decomp: struct-typed signatures 309/10070 (3.1%) at session start ->
474/10088 (4.7%); typed field accesses 0 -> 3547 against 8254 remaining raw dereferences, a 30.1%
field share; modules with a derived request structure 0 -> 7 of 16.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The decompiled artifacts were missing almost all type information. Three things were
available in the tree and only one was being used:
* declare_type was never called, so no type definitions were parsed at all;
* set_type ran only for the four handover types, because fw/libdumps records just
{ea,hash,name,size} and the twelve Windows TA types therefore had no signatures to port;
* the recorded comments were read into a variable and discarded without ever being applied.
All three are now applied, in dependency order, and counted per build so the result is
measurable instead of something you have to notice by reading the output. Across 99 builds:
7727 prototypes (was 1232, all of them ASD), 6068 comments (was zero), 309 struct-typed
signatures (was zero).
The reason prototypes were failing was NOT the missing types, which is what I assumed when I
reported "202 of 1434 rejected" without diagnosing it. Declaring the types first changed the
rejection count by nothing. 205 of the 239 failures were prototypes recorded in the bare
shape 'int()', with no __fastcall for the name-insertion to latch onto, so they were emitted
as the unparsable 'int();'. The name now goes before the first parenthesis whatever the
shape, and prototypes land 100%: 1434/1434 on the ASD reference, 99/99 HDCP, 9/9 DTM.
Worth recording so nobody repeats the detour: the 43 'local_types' in each handover symbol
set are ARM NEON and SVE vector typedefs plus va_list and BOOL -- compiler boilerplate with
no domain structs in it. Declaring them is harmless and buys nothing. The only real PSP
structs in this tree are the seven hand-written ones in fw/ta_ida.py (gcp_ctx, ta_params,
gcp_drm_session, gcp_auth_session, gcp_crypto_session, merit_slot, merit_table), which were
sitting unused and are now applied: GCP command handlers finally read as
'(struct gcp_ctx *ctx, struct ta_params *params)' rather than '(int, int)'.
So the remaining type gap is a data gap, not a pipeline one. Thirteen of the sixteen
components have no struct definitions anywhere in the repo to apply, and ASD's 6800
prototypes are almost entirely primitive-typed, which is why it shows only 30 struct-typed
signatures against 1439 named functions. Closing that needs structs to be defined by hand,
component by component.
|
|
|
The twelve Windows TA types take their symbols from fw/libdumps; ASD, HDCP, RAS and DTM
have theirs in psp_out/handover, in a different shape that also carries prototypes and
comments. This adds that reference path, so every component the corpus holds is covered by
one pipeline rather than twelve of fifteen.
ASD needed its own ELFs. It is driver-class, not a TA: base 0xD00100, and no TA entry
trampoline, so dispatcher-anchored naming does not apply to it at all -- its RPC dispatcher
hangs off trampoline+0x1C instead. The base is not guessed; it matches
psp_out/handover/elf/asd2.elf, whose LOAD segment contains 1562/1562 of the recorded ASD
symbols. The frame discriminator used for the TAs is inapplicable here for the same reason
the trampoline is: there is none, and it reports "neither" for all 17 builds.
Prototypes are applied where recorded. Without them Hex-Rays guesses argument counts and
types, which is most of the distance between readable C and a wall of _DWORD; 1232 of 1434
land on the ASD reference and the rest are rejected by IDA, reported rather than hidden.
One more false-positive class in the dispatcher cross-check, found by running it on HDCP:
the agreement test assumed a ported name encodes its command number, which is GCP's
convention and not HDCP's, so every one of HDCP's 16 commands was reported as conflicting.
A name can only contradict the dispatcher if it claims a command itself, so the check now
requires the ported name to match <ta>_cmd<hex> before judging it. HDCP drops to zero
conflicts while GCP's single real one -- the 0xFE07 handler carrying the inherited name
gcp_cmdFE03_CreateAuthenticatedSession -- is still reported.
Coverage is honest rather than flattering. ASD lands 162-425 named of ~1100-1400 functions
per build, and the dominant limiter is ambiguity, not similarity: 677-908 functions per
build share a byte signature with a differently-named reference function and are skipped
instead of being assigned arbitrarily. ASD is full of identical small stubs, so this is
expected, but it means the ASD figures are a floor and a smarter tie-break would raise
them. RAS is weaker still at 13-20 named per build.
|