mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(apple): trace legacy macOS dylibs with implicit platforms
Apply dyld’s implicit macOS compatibility rule only for a root executable with observed macOS platform and a supported candidate CPU type. Retain the absent platform fields as raw facts and expose the derivation on the candidate. Traverse the selected dylib and report missing required children; preserve unknown platform coverage and the later known fallback graph in other contexts. Validation: 144 source/reader tests, 14 compiled CLI tests, 18 schema tests, full check:ci and docs:check passed. Four source-built app variants passed CLI and SDK-validated stdio MCP checks with unchanged fixture digests. No fixture executable or Apple dyld comparison was run. Independent review resolved a library-root authority gap before integration. Sixteen cold/warm task-cost observations are retained locally with sampling limits; the catalog adds 66 bytes. Actual model tokens and maximum-sized inputs were not measured; no speedup is claimed. Closes #5696
This commit is contained in:
1 parent
ccfaf520b7
commit
90062c0922
4 files changed
+358
-90
No files matched your search
@@ -216,17 +216,26 @@ parser coverage, such as big-endian images), `architecture-missing`,
|
||||
`outside-target`, `escapes-target`, or `undetermined`. Slices follow dyld's
|
||||
graded architectures, so an `x86_64h` process also loads `x86_64`. The edge's `resolution` is one of:
|
||||
|
||||
| Status | Meaning |
|
||||
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `resolved` | An image inside the analyzed root loads, and no earlier candidate was undeterminable. |
|
||||
| `conditional` | An image inside the root resolves, but an earlier candidate outside the root (for example `/usr/lib/swift`) would win if it exists on the running system. |
|
||||
| `unresolved` | Every candidate is definitively absent or unusable inside the root. |
|
||||
| `undetermined` | No candidate resolves inside the root, and some candidate lies outside it or depends on the environment. |
|
||||
| Status | Meaning |
|
||||
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `resolved` | An image inside the analyzed root loads, and no earlier candidate was undeterminable. |
|
||||
| `conditional` | An in-root image may load, but an earlier outside candidate, unmodeled environment input, or unknown platform compatibility can change which image dyld loads. |
|
||||
| `unresolved` | Every candidate is definitively absent or unusable inside the root. |
|
||||
| `undetermined` | No candidate resolves inside the root, and some candidate lies outside it or depends on the environment. |
|
||||
|
||||
A `conditional` image is still traced, because it is the image the analyzed
|
||||
root supplies. Every edge below it has `loader_conditional: true`, and a
|
||||
request that reuses it by install name is `conditional` too. Findings on those
|
||||
edges say that they apply only if the conditional image loads.
|
||||
A `conditional` image is still traced, because it is an in-root candidate
|
||||
that may load. Every edge below it has `loader_conditional: true`, and a
|
||||
request that reuses it by install name is `conditional` too. An in-root dylib
|
||||
with no platform load command remains unknown unless the root is an executable
|
||||
with observed macOS platform and the candidate’s CPU type is x86_64, i386, or arm64. In that macOS context,
|
||||
[Apple's dyld](https://github.com/apple-oss-distributions/dyld/blob/fd8d0c4d52320ebf64db34f3cb280310d905c5ae/mach_o/UnsafeHeader.cpp#L1137-L1145) supplies an implicit macOS platform to legacy dylibs on those CPU
|
||||
types. The candidate records `platform_basis: "macos-dyld-implicit"`; the
|
||||
reported slice fields `platform: null` and `platforms: []` remain the parsed
|
||||
facts. For other process contexts the platform remains unknown, coverage is
|
||||
partial, and the candidate is not treated as incompatible. If a later known
|
||||
candidate resolves, it remains the traced image with conditional resolution;
|
||||
the trace does not claim the unknown candidate's dependency graph. Findings on
|
||||
conditional edges say that they apply only if the conditional image loads.
|
||||
|
||||
Absolute install names that name a path beneath the analyzed root are checked
|
||||
within that root, with symlinks and `..` resolved by the same bounded tree
|
||||
|
||||
@@ -142,6 +142,112 @@ it("uses legacy simulator and device platform facts when resolving dylibs", asyn
|
||||
);
|
||||
});
|
||||
|
||||
it.each([false, true] as const)(
|
||||
"applies macOS dyld's implicit platform to a platform-less dylib and traces its required child present=%s",
|
||||
async (childPresent) => {
|
||||
const root = await createTestTempDirectory("rea-dylib-platform-unknown-");
|
||||
const executablePath = "Contents/MacOS/probe";
|
||||
const dylibPath = "Contents/Frameworks/libA.dylib";
|
||||
const fallbackPath = "Contents/Fallbacks/libA.dylib";
|
||||
const childPath = "Contents/Frameworks/libMissing.dylib";
|
||||
const fallbackChildPath = "Contents/Fallbacks/libFallbackOnly.dylib";
|
||||
const executable = machoImage({
|
||||
cpu: CPU.arm64,
|
||||
commands: [
|
||||
buildVersionCommand(1),
|
||||
rpathCommand("@executable_path/../Frameworks"),
|
||||
rpathCommand("@executable_path/../Fallbacks"),
|
||||
dylibCommand(LC.LOAD_DYLIB, "@rpath/libA.dylib"),
|
||||
],
|
||||
});
|
||||
const dylib = machoImage({
|
||||
cpu: CPU.arm64,
|
||||
fileType: FILE_TYPE.dylib,
|
||||
commands: [
|
||||
rpathCommand("@loader_path"),
|
||||
dylibCommand(LC.LOAD_DYLIB, "@rpath/libMissing.dylib"),
|
||||
],
|
||||
});
|
||||
const child = machoImage({
|
||||
cpu: CPU.arm64,
|
||||
fileType: FILE_TYPE.dylib,
|
||||
commands: [buildVersionCommand(1)],
|
||||
});
|
||||
const fallback = machoImage({
|
||||
cpu: CPU.arm64,
|
||||
fileType: FILE_TYPE.dylib,
|
||||
commands: [
|
||||
rpathCommand("@loader_path"),
|
||||
dylibCommand(LC.LOAD_DYLIB, "@rpath/libFallbackOnly.dylib"),
|
||||
],
|
||||
});
|
||||
const fallbackChild = machoImage({
|
||||
cpu: CPU.arm64,
|
||||
fileType: FILE_TYPE.dylib,
|
||||
commands: [buildVersionCommand(1)],
|
||||
});
|
||||
await mkdir(join(root, "Contents/MacOS"), { recursive: true });
|
||||
await mkdir(join(root, "Contents/Frameworks"), { recursive: true });
|
||||
await mkdir(join(root, "Contents/Fallbacks"), { recursive: true });
|
||||
await writeFile(join(root, executablePath), executable);
|
||||
await writeFile(join(root, dylibPath), dylib);
|
||||
await writeFile(join(root, fallbackPath), fallback);
|
||||
await writeFile(join(root, fallbackChildPath), fallbackChild);
|
||||
if (childPresent) await writeFile(join(root, childPath), child);
|
||||
|
||||
const trace = await traceDylibResolution({
|
||||
rootPath: root,
|
||||
targetPath: join(root, executablePath),
|
||||
targetSha256: createHash("sha256").update(executable).digest("hex"),
|
||||
enumerateRoots: false,
|
||||
parameters: { roots: [executablePath] },
|
||||
});
|
||||
|
||||
expect(
|
||||
trace.images.find(({ path }) => path === executablePath)?.slices[0],
|
||||
).toMatchObject({ platform: 1, platforms: [1] });
|
||||
expect(
|
||||
trace.images.find(({ path }) => path === dylibPath)?.slices[0],
|
||||
).toMatchObject({ platform: null, platforms: [] });
|
||||
expect(trace.edges[0]).toMatchObject({
|
||||
loader: executablePath,
|
||||
install_name: "@rpath/libA.dylib",
|
||||
candidates: [
|
||||
{
|
||||
outcome: "resolved",
|
||||
resolved_path: dylibPath,
|
||||
platform_basis: "macos-dyld-implicit",
|
||||
},
|
||||
],
|
||||
resolution: { status: "resolved", image: dylibPath },
|
||||
});
|
||||
expect(trace.edges[1]).toMatchObject({
|
||||
loader: dylibPath,
|
||||
install_name: "@rpath/libMissing.dylib",
|
||||
resolution: childPresent
|
||||
? { status: "resolved", image: childPath }
|
||||
: { status: "unresolved", image: null },
|
||||
loader_conditional: false,
|
||||
});
|
||||
expect(trace.images.map(({ path }) => path)).toEqual(
|
||||
childPresent
|
||||
? [executablePath, childPath, dylibPath].sort()
|
||||
: [executablePath, dylibPath].sort(),
|
||||
);
|
||||
expect(trace.images.map(({ path }) => path)).not.toContain(fallbackPath);
|
||||
expect(trace.images.map(({ path }) => path)).not.toContain(
|
||||
fallbackChildPath,
|
||||
);
|
||||
expect(trace.coverage.status).toBe("complete");
|
||||
expect(trace.limitations).toContainEqual(
|
||||
expect.stringContaining(dylibPath),
|
||||
);
|
||||
expect(trace.findings.map(({ kind }) => kind)).toEqual(
|
||||
childPresent ? [] : ["required-load-unresolved"],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
describe("absolute dyld candidate paths at the filesystem reader boundary", () => {
|
||||
it.each(["install-name", "rpath"] as const)(
|
||||
"resolves an absolute in-root %s while preserving its source path",
|
||||
|
||||
@@ -441,26 +441,6 @@ describe("dyld resolution outcomes: checking candidate format and compatibility"
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves unknown platform compatibility before a later known candidate", async () => {
|
||||
const trace = await traceDylibLoading(
|
||||
memoryView({
|
||||
[MAIN]: executable({
|
||||
rpaths: ["@executable_path/a", "@executable_path/b"],
|
||||
dependencies: [dependency("@rpath/lib.dylib")],
|
||||
}),
|
||||
"Contents/MacOS/a/lib.dylib": parsed(slice({ platform: null })),
|
||||
"Contents/MacOS/b/lib.dylib": parsed(slice()),
|
||||
}),
|
||||
{ roots: [MAIN] },
|
||||
);
|
||||
const edge = edgeFor(trace, MAIN, "@rpath/lib.dylib");
|
||||
expect(edge.candidates.map(({ outcome }) => outcome)).toEqual([
|
||||
"undetermined",
|
||||
"resolved",
|
||||
]);
|
||||
expect(edge.resolution.status).toBe("conditional");
|
||||
});
|
||||
|
||||
it("applies dyld Catalyst and zippered platform compatibility", async () => {
|
||||
const catalystProcess = await traceDylibLoading(
|
||||
memoryView({
|
||||
@@ -506,6 +486,104 @@ describe("dyld resolution outcomes: checking candidate format and compatibility"
|
||||
});
|
||||
});
|
||||
|
||||
describe("platform-less dylib compatibility", () => {
|
||||
it("keeps an unknown candidate unresolved and traces the later known fallback", async () => {
|
||||
const trace = await traceDylibLoading(
|
||||
memoryView({
|
||||
[MAIN]: executable({
|
||||
platform: 6,
|
||||
platforms: [6],
|
||||
rpaths: ["@executable_path/a", "@executable_path/b"],
|
||||
dependencies: [dependency("@rpath/lib.dylib")],
|
||||
}),
|
||||
"Contents/MacOS/a/lib.dylib": parsed(slice({ platform: null })),
|
||||
"Contents/MacOS/b/lib.dylib": parsed(
|
||||
slice({
|
||||
platform: 6,
|
||||
platforms: [6],
|
||||
dependencies: [dependency("@loader_path/libChild.dylib")],
|
||||
}),
|
||||
),
|
||||
"Contents/MacOS/b/libChild.dylib": parsed(
|
||||
slice({ platform: 6, platforms: [6] }),
|
||||
),
|
||||
}),
|
||||
{ roots: [MAIN] },
|
||||
);
|
||||
const edge = edgeFor(trace, MAIN, "@rpath/lib.dylib");
|
||||
expect(edge.candidates.map(({ outcome }) => outcome)).toEqual([
|
||||
"undetermined",
|
||||
"resolved",
|
||||
]);
|
||||
expect(edge.resolution).toEqual({
|
||||
status: "conditional",
|
||||
image: "Contents/MacOS/b/lib.dylib",
|
||||
});
|
||||
expect(edge.candidates[0]).not.toHaveProperty("platform_basis");
|
||||
expect(trace.edges.map(({ loader }) => loader)).toContain(
|
||||
"Contents/MacOS/b/lib.dylib",
|
||||
);
|
||||
expect(trace.images.map(({ path }) => path)).toContain(
|
||||
"Contents/MacOS/b/libChild.dylib",
|
||||
);
|
||||
expect(trace.coverage.status).toBe("partial");
|
||||
expect(trace.limitations).toContainEqual(
|
||||
expect.stringContaining("Contents/MacOS/a/lib.dylib"),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not infer a platform-less image's platform when process platform is unknown", async () => {
|
||||
const trace = await traceDylibLoading(
|
||||
memoryView({
|
||||
[MAIN]: executable({
|
||||
platform: null,
|
||||
platforms: [],
|
||||
rpaths: ["@executable_path/a"],
|
||||
dependencies: [dependency("@rpath/lib.dylib")],
|
||||
}),
|
||||
"Contents/MacOS/a/lib.dylib": parsed(slice({ platform: null })),
|
||||
}),
|
||||
{ roots: [MAIN] },
|
||||
);
|
||||
const edge = edgeFor(trace, MAIN, "@rpath/lib.dylib");
|
||||
expect(edge.candidates[0]).toMatchObject({
|
||||
outcome: "undetermined",
|
||||
resolved_path: "Contents/MacOS/a/lib.dylib",
|
||||
});
|
||||
expect(edge.candidates[0]).not.toHaveProperty("platform_basis");
|
||||
expect(edge.resolution).toEqual({ status: "undetermined", image: null });
|
||||
expect(trace.coverage.status).toBe("partial");
|
||||
});
|
||||
|
||||
it("does not treat a selected dylib root as an executable process context", async () => {
|
||||
const library = "Contents/Frameworks/libRoot.dylib";
|
||||
const child = "Contents/Frameworks/libChild.dylib";
|
||||
const trace = await traceDylibLoading(
|
||||
memoryView({
|
||||
[library]: parsed(
|
||||
slice({
|
||||
file_type: "dylib",
|
||||
file_type_code: 6,
|
||||
platform: 1,
|
||||
platforms: [1],
|
||||
dependencies: [dependency("@loader_path/libChild.dylib")],
|
||||
}),
|
||||
),
|
||||
[child]: parsed(slice({ platform: null })),
|
||||
}),
|
||||
{ roots: [library] },
|
||||
);
|
||||
const edge = edgeFor(trace, library, "@loader_path/libChild.dylib");
|
||||
expect(edge.candidates[0]).toMatchObject({
|
||||
outcome: "undetermined",
|
||||
resolved_path: child,
|
||||
});
|
||||
expect(edge.candidates[0]).not.toHaveProperty("platform_basis");
|
||||
expect(edge.resolution).toEqual({ status: "undetermined", image: null });
|
||||
expect(trace.coverage.status).toBe("partial");
|
||||
});
|
||||
});
|
||||
|
||||
describe("dyld load order", () => {
|
||||
it("loads each image once per process and reuses matching install names", async () => {
|
||||
const a = "Contents/Frameworks/libA.dylib";
|
||||
|
||||
@@ -51,6 +51,13 @@ const dependencyCommandSchema = z.enum([
|
||||
"LC_LOAD_UPWARD_DYLIB",
|
||||
]);
|
||||
|
||||
const IMPLICIT_MACOS_PLATFORM = 1;
|
||||
const MACOS_DYLD_IMPLICIT_PLATFORM_CPU_TYPES = new Set([
|
||||
7, // CPU_TYPE_I386
|
||||
0x01000007, // CPU_TYPE_X86_64
|
||||
0x0100000c, // CPU_TYPE_ARM64, including arm64 subtypes
|
||||
]);
|
||||
|
||||
const dependencySchema = z.strictObject({
|
||||
command: dependencyCommandSchema,
|
||||
encoding: z.enum([
|
||||
@@ -145,6 +152,7 @@ const candidateSchema = z.strictObject({
|
||||
"undetermined",
|
||||
]),
|
||||
resolved_path: z.string().nullable(),
|
||||
platform_basis: z.enum(["macos-dyld-implicit"]).optional(),
|
||||
});
|
||||
|
||||
const edgeSchema = z.strictObject({
|
||||
@@ -227,7 +235,7 @@ interface LoadedImage {
|
||||
readonly slice: MachoSlice;
|
||||
/** Images from the process root to this image; the rpath stack, outermost first. */
|
||||
readonly chain: readonly string[];
|
||||
/** Some edge on the chain was conditional: an unknown earlier candidate may load instead. */
|
||||
/** Some edge on the chain has unresolved candidate or platform facts. */
|
||||
readonly conditional: boolean;
|
||||
}
|
||||
|
||||
@@ -240,6 +248,8 @@ interface ProcessContext {
|
||||
>;
|
||||
readonly searchOverrides: readonly DyldSearchOverride[];
|
||||
readonly environmentRoots: Set<string>;
|
||||
readonly unknownPlatformCandidates: Set<string>;
|
||||
readonly implicitMacOSPlatformCandidates: Set<string>;
|
||||
readonly platforms: readonly number[];
|
||||
readonly executable: string | null;
|
||||
readonly loaded: Map<string, LoadedImage>;
|
||||
@@ -346,47 +356,75 @@ const evaluateCandidate = async (
|
||||
// also admits main executables and bundles for different operations.
|
||||
else if (slice.file_type_code !== 6) outcome = "not-loadable";
|
||||
else {
|
||||
const platformResult = platformLoadability(
|
||||
context.platforms,
|
||||
slice.platforms,
|
||||
slice.file_type_code,
|
||||
context.processIdentity.architecture,
|
||||
);
|
||||
const platformResult = platformLoadability(context, slice);
|
||||
outcome =
|
||||
platformResult === "compatible"
|
||||
platformResult.outcome === "compatible"
|
||||
? "resolved"
|
||||
: platformResult === "incompatible"
|
||||
: platformResult.outcome === "incompatible"
|
||||
? "platform-mismatch"
|
||||
: "undetermined";
|
||||
if (outcome === "undetermined")
|
||||
context.unknownPlatformCandidates.add(lookup.path);
|
||||
if (platformResult.basis !== undefined) {
|
||||
context.implicitMacOSPlatformCandidates.add(lookup.path);
|
||||
return {
|
||||
...base,
|
||||
outcome,
|
||||
resolved_path: lookup.path,
|
||||
platform_basis: platformResult.basis,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return { ...base, outcome, resolved_path: lookup.path };
|
||||
};
|
||||
|
||||
type PlatformLoadability = "compatible" | "incompatible" | "unknown";
|
||||
type PlatformLoadabilityResult = {
|
||||
readonly outcome: PlatformLoadability;
|
||||
readonly basis?: Candidate["platform_basis"];
|
||||
};
|
||||
|
||||
/** Apply dyld's documented cross-platform load cases and retain uncertain host cases. */
|
||||
const platformLoadability = (
|
||||
processPlatforms: readonly number[],
|
||||
imagePlatforms: readonly number[],
|
||||
fileType: number,
|
||||
architecture: string,
|
||||
): PlatformLoadability => {
|
||||
if (processPlatforms.length === 0 || imagePlatforms.length === 0)
|
||||
return "unknown";
|
||||
context: Pick<ProcessContext, "platforms" | "executable">,
|
||||
image: MachoSlice,
|
||||
): PlatformLoadabilityResult => {
|
||||
const { platforms: processPlatforms } = context;
|
||||
const { file_type_code: fileType, platforms: imagePlatforms } = image;
|
||||
let effectiveImagePlatforms = imagePlatforms;
|
||||
let basis: Candidate["platform_basis"];
|
||||
if (imagePlatforms.length === 0) {
|
||||
// macOS dyld supplies an implicit macOS platform to old dylibs on these
|
||||
// CPU types. Keep the parsed platform facts untouched; this is only the
|
||||
// compatibility interpretation used for an explicitly macOS process.
|
||||
if (
|
||||
context.executable !== null &&
|
||||
processPlatforms.length === 1 &&
|
||||
processPlatforms[0] === IMPLICIT_MACOS_PLATFORM &&
|
||||
MACOS_DYLD_IMPLICIT_PLATFORM_CPU_TYPES.has(image.cpu_type)
|
||||
) {
|
||||
effectiveImagePlatforms = [IMPLICIT_MACOS_PLATFORM];
|
||||
basis = "macos-dyld-implicit";
|
||||
} else return { outcome: "unknown" };
|
||||
}
|
||||
if (processPlatforms.length === 0) return { outcome: "unknown" };
|
||||
const outcomes = processPlatforms.map((processPlatform) =>
|
||||
imageLoadabilityForPlatform(
|
||||
processPlatform,
|
||||
imagePlatforms,
|
||||
effectiveImagePlatforms,
|
||||
fileType,
|
||||
architecture,
|
||||
image.architecture,
|
||||
),
|
||||
);
|
||||
if (outcomes.every((outcome) => outcome === "compatible"))
|
||||
return "compatible";
|
||||
return { outcome: "compatible", ...(basis === undefined ? {} : { basis }) };
|
||||
if (outcomes.every((outcome) => outcome === "incompatible"))
|
||||
return "incompatible";
|
||||
return "unknown";
|
||||
return {
|
||||
outcome: "incompatible",
|
||||
...(basis === undefined ? {} : { basis }),
|
||||
};
|
||||
return { outcome: "unknown", ...(basis === undefined ? {} : { basis }) };
|
||||
};
|
||||
|
||||
const imageLoadabilityForPlatform = (
|
||||
@@ -572,7 +610,7 @@ const traceProcess = async (
|
||||
context.loaded.set(image, {
|
||||
slice,
|
||||
chain: [...current.chain, image],
|
||||
// dyld may load an unknown earlier candidate instead of this fallback.
|
||||
// A modeled earlier search uncertainty can make this image conditional.
|
||||
conditional:
|
||||
current.conditional || edge.resolution.status === "conditional",
|
||||
});
|
||||
@@ -589,6 +627,69 @@ const traceProcess = async (
|
||||
return edges;
|
||||
};
|
||||
|
||||
const projectTraceResult = (input: {
|
||||
readonly images: ReadonlyMap<string, MachoImageFacts>;
|
||||
readonly roots: DylibTrace["roots"];
|
||||
readonly rootSlices: readonly TracedRootSlice[];
|
||||
readonly edges: readonly Edge[];
|
||||
readonly withoutArchitecture: readonly string[];
|
||||
readonly environmentRoots: ReadonlySet<string>;
|
||||
readonly unknownPlatformCandidates: ReadonlySet<string>;
|
||||
readonly implicitMacOSPlatformCandidates: ReadonlySet<string>;
|
||||
}): DylibTrace => {
|
||||
const reported = [...input.images.entries()]
|
||||
.filter(([, facts]) => facts.status !== "not-mach-o")
|
||||
.sort(([left], [right]) => compare(left, right));
|
||||
const unparsed = reported
|
||||
.filter(([, facts]) => facts.status !== "parsed")
|
||||
.map(([path]) => path);
|
||||
return {
|
||||
roots: input.roots,
|
||||
images: reported.map(([path, facts]) => ({
|
||||
path,
|
||||
parse_status:
|
||||
facts.status === "not-mach-o" ? "unsupported" : facts.status,
|
||||
reason:
|
||||
facts.status === "parsed" || facts.status === "not-mach-o"
|
||||
? null
|
||||
: facts.reason,
|
||||
slices:
|
||||
facts.status === "parsed" ? facts.slices.map(withoutDependencies) : [],
|
||||
})),
|
||||
edges: [...input.edges],
|
||||
findings: deriveFindings(input.edges, input.rootSlices),
|
||||
coverage: {
|
||||
status:
|
||||
unparsed.length === 0 &&
|
||||
input.withoutArchitecture.length === 0 &&
|
||||
input.environmentRoots.size === 0 &&
|
||||
input.unknownPlatformCandidates.size === 0
|
||||
? "complete"
|
||||
: "partial",
|
||||
unparsed_images: unparsed,
|
||||
roots_without_architecture: [...input.withoutArchitecture],
|
||||
},
|
||||
limitations: [
|
||||
...DYLIB_RESOLUTION_LIMITATIONS,
|
||||
...(input.environmentRoots.size === 0
|
||||
? []
|
||||
: [
|
||||
`Roots ${[...input.environmentRoots].join(", ")} request applicable image-selection inputs through LC_DYLD_ENVIRONMENT that are not modeled; affected dependencies or inserted images remain uncertain.`,
|
||||
]),
|
||||
...(input.unknownPlatformCandidates.size === 0
|
||||
? []
|
||||
: [
|
||||
`In-root dylib candidates ${[...input.unknownPlatformCandidates].sort(compare).join(", ")} have unknown platform compatibility and are not treated as incompatible. Their dependent graphs may be incomplete.`,
|
||||
]),
|
||||
...(input.implicitMacOSPlatformCandidates.size === 0
|
||||
? []
|
||||
: [
|
||||
`In-root dylib candidates ${[...input.implicitMacOSPlatformCandidates].sort(compare).join(", ")} have no observed platform load command; macOS compatibility was derived from dyld's implicit-platform rule for an executable root with explicit macOS platform and a supported CPU type. Reported platform facts remain unchanged.`,
|
||||
]),
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
/** Trace dyld load paths from each process root through the analyzed root. */
|
||||
export const traceDylibLoading = async (
|
||||
view: DylibTreeView,
|
||||
@@ -608,6 +709,8 @@ export const traceDylibLoading = async (
|
||||
const edges: Edge[] = [];
|
||||
const withoutArchitecture: string[] = [];
|
||||
const environmentRoots = new Set<string>();
|
||||
const unknownPlatformCandidates = new Set<string>();
|
||||
const implicitMacOSPlatformCandidates = new Set<string>();
|
||||
for (const root of request.roots) {
|
||||
const facts = await view.image(root);
|
||||
images.set(root, facts);
|
||||
@@ -647,6 +750,8 @@ export const traceDylibLoading = async (
|
||||
},
|
||||
searchOverrides,
|
||||
environmentRoots,
|
||||
unknownPlatformCandidates,
|
||||
implicitMacOSPlatformCandidates,
|
||||
platforms: slice.platforms,
|
||||
executable,
|
||||
loaded: new Map(),
|
||||
@@ -659,46 +764,16 @@ export const traceDylibLoading = async (
|
||||
);
|
||||
}
|
||||
}
|
||||
const reported = [...images.entries()]
|
||||
.filter(([, facts]) => facts.status !== "not-mach-o")
|
||||
.sort(([left], [right]) => compare(left, right));
|
||||
const unparsed = reported
|
||||
.filter(([, facts]) => facts.status !== "parsed")
|
||||
.map(([path]) => path);
|
||||
return {
|
||||
return projectTraceResult({
|
||||
images,
|
||||
roots,
|
||||
images: reported.map(([path, facts]) => ({
|
||||
path,
|
||||
parse_status:
|
||||
facts.status === "not-mach-o" ? "unsupported" : facts.status,
|
||||
reason:
|
||||
facts.status === "parsed" || facts.status === "not-mach-o"
|
||||
? null
|
||||
: facts.reason,
|
||||
slices:
|
||||
facts.status === "parsed" ? facts.slices.map(withoutDependencies) : [],
|
||||
})),
|
||||
rootSlices,
|
||||
edges,
|
||||
findings: deriveFindings(edges, rootSlices),
|
||||
coverage: {
|
||||
status:
|
||||
unparsed.length === 0 &&
|
||||
withoutArchitecture.length === 0 &&
|
||||
environmentRoots.size === 0
|
||||
? "complete"
|
||||
: "partial",
|
||||
unparsed_images: unparsed,
|
||||
roots_without_architecture: withoutArchitecture,
|
||||
},
|
||||
limitations: [
|
||||
...DYLIB_RESOLUTION_LIMITATIONS,
|
||||
...(environmentRoots.size === 0
|
||||
? []
|
||||
: [
|
||||
`Roots ${[...environmentRoots].join(", ")} request applicable image-selection inputs through LC_DYLD_ENVIRONMENT that are not modeled; affected dependencies or inserted images remain uncertain.`,
|
||||
]),
|
||||
],
|
||||
};
|
||||
withoutArchitecture,
|
||||
environmentRoots,
|
||||
unknownPlatformCandidates,
|
||||
implicitMacOSPlatformCandidates,
|
||||
});
|
||||
};
|
||||
|
||||
const withoutDependencies = (
|
||||
|
||||
Reference in new issue
Block a user