Carry #952's Apple ownership into the cache work: the cache reader and
fixture move to src/artifacts/apple/, the cache facts, platforms and
resolution schema to src/domain/apple/, the cache service to
src/application/apple/, and the lane to scripts/verify/apple/.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follow #952's Apple ownership: the Mach-O load-command reader, dylib
resolution adapter and fixture move to src/artifacts/apple/, the resolver,
dyld path semantics and findings to src/domain/apple/, and the dylib lane
helper to scripts/verify/apple/.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Carry conditional-load propagation, parsed-image digest binding, and
segment-wise root resolution into the shared-cache trace. Also:
- Make cache compatibility directional: an arm64e cache serves arm64
processes and an x86_64h cache serves x86_64 ones, not the reverse.
- Require the complete 24-byte LC_BUILD_VERSION with its declared tool
records, and the 16-byte LC_VERSION_MIN_* command, before recording a
platform.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Edges below a conditional fallback now carry loader_conditional, reuse of
a conditional image by install name is conditional, and their findings say
they apply only if that image loads.
- Hash each traced image only if it is still the file whose headers were
parsed (device, inode, size and change times), before and after hashing.
- Resolve explicit roots segment by segment, so a root below a symlinked
directory that points outside the analyzed root is rejected.
- Require a complete 16-byte LC_CODE_SIGNATURE command.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Use a supplied cache for a process only when it serves the process's CPU
family and one of its LC_BUILD_VERSION or LC_VERSION_MIN platforms matches
the cache's platform or alternate platform; slices now report platforms.
A limitation names each root the cache was not used for, and why.
- Treat an image listed in the cache but mapped in a missing or
UUID-mismatched subcache as undetermined, list it in
coverage.unverified_shared_cache_images, and mark coverage partial.
- Keep a cache hit conditional when an earlier candidate is undeterminable.
- Open a development cache's named subcaches beside its base name.
- Describe absolute paths as not evaluated only when no cache is supplied.
- Preflight dyld_info in verify:dyld-shared-cache.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address review findings in trace_dylib_resolution:
- Resolve LC_LAZY_LOAD_DYLIB dependencies without traversing them, since
dyld loads them on first use. An unresolved one becomes
`lazy-load-unresolved`, not a launch failure.
- Accept FAT tables up to 128 records, as target opening does. A table
that names no Mach-O CPU type, such as a Java class file, is not
Mach-O.
- Require 8-byte load-command alignment in 64-bit images. 1,508 real
slices still match otool.
- Report EACCES/EPERM while probing, reading, or hashing as an
`unavailable` failure naming the code and path, rather than generic
io.
Findings and limitations move to dylibResolutionFindings.ts to keep the
resolver under the module size limit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
macOS 11 and later ship system libraries only inside the dyld shared
cache. trace_dylib_resolution therefore left every /usr/lib and /System
load undetermined, and REA had no way to see what a cache contains.
inspect_dyld_shared_cache (CLI inspect-dyld-shared-cache) reads a cache
file set selected by path, without an open target. Its pure TypeScript
reader parses modern and legacy dyld_cache_header layouts and reports:
- UUID, architecture, platform, OS version, Mac Catalyst alternate,
cache type, shared region, max slide;
- every VM mapping;
- each subcache by file suffix, checked by UUID. A missing or
mismatched subcache makes coverage partial;
- the complete image list.
For each requested install path it reads that image's load commands
through the cache mappings with the existing Mach-O reader.
trace_dylib_resolution accepts an optional shared_cache. Absolute
candidates found in a cache that serves the process's CPU family resolve
with the new outcome and status `shared-cache`. That status also takes
precedence over later in-bundle fallbacks such as a back-deployed
libswiftCore. The result names the cache UUID and OS version it used.
verify:dyld-shared-cache checks every image in the host cache (3,649 on
macOS 26.6), with dependencies and their re-export, weak, upward and
delay-init attributes, against `dyld_info -all_dyld_cache
-linked_dylibs`. It also checks CLI/MCP parity, and that each
shared-cache edge in the fixture trace is a library dyld actually loads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Address review findings in trace_dylib_resolution:
- Choose child slices by dyld's graded architectures, so an x86_64h
process loads a generic x86_64 slice instead of reporting
architecture-missing.
- Read FAT containers with the byte-swapped FAT_CIGAM and FAT_CIGAM_64
magics, which target opening already accepts.
- Report string-bearing load commands too short to hold their string
offset as malformed instead of failing with a RangeError.
- Keep recognized but unsupported images as an `unsupported` candidate
outcome. It is undeterminable like an outside-root path, not
malformed.
- List Mach-O files that do not parse during root enumeration as
unparsed images, so coverage is partial rather than silently complete.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add trace_dylib_resolution (CLI trace-dylib-resolution) to the artifact
family. For the active Mach-O, or every executable in the active .app
(the main app, XPC services, extensions, login items, helpers), it
reports which file each dylib load reaches and how.
A pure TypeScript reader decodes FAT and Mach-O load commands:
LC_LOAD/WEAK/REEXPORT/LAZY/UPWARD_DYLIB, including the newer
dylib_use_command encoding with weak, re-export, upward and delayed-init
flags, plus LC_ID_DYLIB, LC_RPATH, LC_DYLD_ENVIRONMENT and code-signature
presence. Malformed tables are reported with the failing command instead
of being guessed.
Resolution follows dyld:
- @executable_path comes from each process root;
- @loader_path comes from the loading image, or from the owner of the
LC_RPATH entry being expanded;
- @rpath searches the loader's rpaths first, then up the load chain;
- dependencies load dependents-first in load-command order, once per
process, and a matching install name reuses an already loaded image;
- symlinks are followed inside the analyzed root, and escapes stop.
Every candidate path is reported with its outcome. Each edge resolves to
resolved, conditional (an outside-root candidate such as /usr/lib/swift
precedes), unresolved, or undetermined. Absolute and shared-cache system
paths are never checked against the host. Derived findings cover
unresolved required or weak loads, absent earlier @rpath candidates (with
library validation left unknown), and LC_DYLD_ENVIRONMENT.
verify:macos-bundle now also:
- checks every traced image against otool -l;
- asserts each resolution status on a clang-built fixture that adds a
missing Overrides rpath, an absolute rpath, and a delay-init library;
- compares the predicted load order of two process roots with what dyld
loads under DYLD_PRINT_LIBRARIES.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>