Files
rea/docs/gpu-code-objects.md
T
morluto 5f702dd6cc fix(parsers): preserve format boundaries and query occurrence identity
Normalize parsed facts at their owning boundaries across PE exports and certificates, ELF section names, GPU bundles, IL2CPP generic types, dyld slice selection, source maps, environment replay, and JavaScript mapped arguments. Preserve partial facts and explicit unsupported cases; clarify the corresponding documentation.

Validation: 831 affected tests passed with one existing skip across 23 files; build, typecheck/lint, documentation, formatting, and 2133-file test discovery passed. Format controls are synthetic; real Unity, GPU, and ARM-device execution is unverified.

Fixes #6709
Fixes #6688
Fixes #6719
Fixes #6718
Fixes #6704
Fixes #6703
Fixes #6692
Fixes #6723
Fixes #6687
Fixes #6726
Fixes #6696
Fixes #6691
Fixes #6727
2026-10-23 22:01:16 +08:00

9.9 KiB
Raw Blame History

Static CUDA and HIP code objects

inspect_gpu_code_objects answers which GPU targets and kernel declarations a local artifact carries. It reads ELF/PE sections, standalone CUDA fatbins, CUBIN/PTX and Clang HIP bundles, including CCOB v1/v2/v3. It neither executes the artifact nor invokes CUDA/ROCm tools. GPU execution, SASS disassembly and host launch correlation remain outside coverage.

Start with a target inventory:

{ "path": "/artifacts/library.so", "view": { "collection": "targets" } }

Target rows aggregate inventoried payloads and distinguish an unrequested kernel analysis from a complete or partial one. objects pages expose exact object IDs, compression declarations and encoded/header locations. Fields, exact filters, batch IDs and stable pagination select the facts delivered to the caller. IDs are positional within the recorded artifact SHA-256. Selecting object_ids in a new inspection requires expected_sha256 from that inventory; a changed file is rejected before parsing selected payloads.

target is the inventory declaration used for selection; payload_target is the independently observed inner declaration when available. Object pages keep both values, their kind, target_consistency and inspection coverage even with selected fields. Conflicting base architectures retain both values, mark object/report/target coverage partial and annotate the affected kernels. CUBIN flags establish a base architecture: base-only explicitly leaves feature-profile agreement unknown, including a and f variants. A standalone declaration is single-source, and unexamined payloads remain unexamined. An exact AMDGPU target is normalized at its note owner for comparison with a Clang bundle declaration; raw values remain intact. Unresolved feature comparisons stay unknown.

HIP bundle recognition accepts both the legacy amdgcn triple and LLVM's amdgpu subarchitecture triples, including declared generic processors such as gfx11-generic and gfx9-4-generic. CUDA's legacy fatbinary compression flag is retained as an unsupported codec with its encoded and decoded extents; compressed bytes are not treated as PTX text. CCOB v1 has no stored total size, so a following legacy bundle is located by the next CCOB signature and zero-byte alignment padding remains part of the first container extent.

cuda_target_header retains the OS ABI, normalized raw e_flags and exact header location. CUDA OS ABI 0x33 and the observed 0x41 flags layout use different architecture fields; they are decoded at one ABI-aware owner. Unrecognized layouts keep the header and an unknown target instead of inventing an SM label. Header provenance is validated against its CUBIN object.

Target filters select inventory declarations. An unselected payload's inner target remains unexamined; an empty filtered result does not establish that no enclosed code declares that target. Discovering every inner declaration requires decoding those payloads.

HIP bundle IDs retain the declared processor and v2-v3 +feature suffixes for exact selection. If a recognized AMDGPU bundle ID does not expose a target, the same bounded note-discovery owner used for standalone AMDGPU files establishes it before filtering. Those directory/note reads share the inspection work budget; successive queries can select the target returned by inventory.

Decode selected targets and return a kernel page in the same call:

{
  "path": "/artifacts/library.so",
  "analysis": "kernels",
  "targets": ["sm_100"],
  "view": {
    "collection": "kernels",
    "names": ["kernel_name"],
    "fields": [
      "name",
      "parameter_count",
      "parameter_bytes",
      "parameters",
      "registers",
      "shared_bytes"
    ]
  }
}

Only selected payloads are decoded. Other inventory entries remain present with not-selected states, and overall payload coverage remains partial. An empty kernel page proves no matching retained fact; it does not establish that an unexamined payload contains no kernel. Missing fields remain null.

Kernel representations have different meanings:

  • PTX reports parameter declarations and virtual registers. Shared bytes cover supported static declarations inside that entry; they do not establish module shared memory, dynamic allocation or physical device resources. PTX 1.0–1.3 declares entry parameters in the body; PTX 1.4 and later uses the entry parameter list. Entry-scoped .pragma statements before the body do not end the declaration or suppress its body resources. A .target compute_xx spelling is retained as written; NVIDIA PTX defines it as a synonym for .target sm_xx when comparing its target identity with the fatbin declaration. .callprototype formal parameters are excluded from entry virtual-register counts. Parameter names remain available when pointer qualifiers are understood or an unsupported qualifier prevents layout decoding. A pointer's .align describes the pointed-to memory and does not change parameter layout alignment. Parameter names and following declarations are retained when an array extent uses a parenthesized expression or when dimensions are multidimensional, even though those forms do not currently yield a layout. PTX target selection uses the highest-numbered module-scope .target directive, including directives after entry declarations; unsupported or conflicting target profiles leave the effective target unknown. Scalar register declarations count each bare or percent-prefixed name and each parameterized range. Unsupported declaration forms or overflowing totals retain an unknown count and partial decoding coverage. Unsupported parameter layouts and unknown shared-memory declarations likewise retain their known facts, explicit limitations and partial coverage through retained pages.
  • CUBIN identifies CUDA entry symbols and reads numeric nv.info records, including V2 kernel-parameter sizes, and native section declarations. Global register declarations also cover compiler versions that leave the older text-section register field empty.
  • AMDGPU reads v3+ MessagePack notes. Argument counts include hidden ABI arguments when present; SGPR/VGPR counts and fixed group/private segment sizes retain their declared meanings. Legacy YAML metadata remains unsupported.

Each selected PTX payload becomes a retained UTF-8 text artifact with a SHA-256, byte length and source coordinates. It is omitted from compact replies. Read it with inspect_analysis_view and the parent Evidence ID returned by the compact receipt:

{
  "source": { "kind": "retained-evidence", "evidence_id": "ev_..." },
  "view": {
    "kind": "gpu-code-objects",
    "collection": "ptx",
    "object_id": "...",
    "offset": 0,
    "limit": 4096
  }
}

Text offsets use UTF-16 code units and must fall on Unicode scalar boundaries. Complete Evidence retains the selected text; export_evidence_bundle provides its portable source record. Historical pages never reopen the input file.

A location with container_id: null uses original file offsets. Other locations use that inventoried object's decoded byte coordinates. Follow its parent, encoded location and digest to trace decompressed metadata back to a native section and the original file. Decoded offsets are never presented as physical file offsets. Reads from an ELF or PE GPU section stay within that declared section extent, so neighboring native section bytes cannot complete a truncated embedded bundle header.

The CLI uses the same service and contracts:

rea inspect-gpu-code-objects /artifacts/library.so --analysis kernels \
  --targets '["sm_100"]' --detail summary \
  --view '{"collection":"kernels","names":["kernel_name"]}' \
  --evidence_output /analysis/library-gpu.evidence.json --json

A full-file SHA-256 always streams every byte. Inventory then reads bounded native directories and bundle headers; CCOB requires decompression to discover its inner directory. Kernel analysis reads selected payloads. bytes_read includes hashing and subsequent descriptor reads, so this is not a header-only hashing workflow.

CUDA raw LZ4 blocks and Zstd, and CCOB zlib/Zstd are supported. Unknown versions or codecs retain declarations with explicit limitations. Lengths are checked before decoding, and CCOB's truncated MD5 is verified against decoded bytes. Resource limits are one active inspection, 128 MiB per input/output buffer, 512 MiB of payload input, decoded output and AMD note-read work per inspection, and 16 MiB retained facts including selected PTX. Payload work is reserved before reading or allocating; a capacity refusal does not reread an aliased payload. Native tables and decoded section names each have a 16 MiB structural budget; AMDGPU ELF note discovery admits at most 8 MiB of source note bytes, with bounded depth and container/string allocations. Standalone AMD targets are read from those notes before target selection; selected kernel analysis reuses the same parsed declarations. Pages have a 1 MiB response budget. Resource refusals and interrupted observations preserve known facts and explicit uncertainty.

CUDA ELF parsing additionally shares a 262,144 record/symbol visit limit, 128 MiB of cumulative metadata section reads, and a 16 MiB serialized-size accounting budget for temporary index facts. These limits are checked before index growth; they are not claims about JavaScript heap size. Equivalent scoped metadata byte ranges and repeated equal register declarations are coalesced; conflicting values remain unknown. Parameter sections are parsed once per ELF.

Missing CUBIN symbol tables or unresolved section names produce partial object, report and target coverage even when some kernel facts survive. AMD note failures preserve earlier successfully parsed declarations and the original failure reason. Standalone PTX recognition tries a small header first, then continues within the existing 16 MiB text budget when leading comments or whitespace obscure its directives; admitted full-file probe bytes are reused.