diff --git a/src/domain/apple/dylibResolution.test.ts b/src/domain/apple/dylibResolution.test.ts index d24e9d36e..93916825b 100644 --- a/src/domain/apple/dylibResolution.test.ts +++ b/src/domain/apple/dylibResolution.test.ts @@ -755,7 +755,13 @@ describe("conditional loads", () => { [VENDOR]: parsed( slice({ install_name: "@rpath/libvendor.dylib", - dependencies: [dependency("@loader_path/libgone.dylib")], + dependencies: [ + dependency("@loader_path/libgone.dylib"), + dependency("@loader_path/libweak.dylib", { weak: true }), + dependency("@loader_path/liblazy.dylib", { + command: "LC_LAZY_LOAD_DYLIB", + }), + ], }), ), [OTHER]: parsed( @@ -777,9 +783,14 @@ describe("conditional loads", () => { resolution: { status: "conditional", image: VENDOR }, loader_conditional: false, }); - expect( - trace.findings.find(({ kind }) => kind === "required-load-unresolved") - ?.explanation, - ).toContain(`${VENDOR} loads only conditionally`); + // Required, weak and lazy findings below the fallback are all qualified. + for (const kind of [ + "required-load-unresolved", + "weak-load-unresolved", + "lazy-load-unresolved", + ]) + expect( + trace.findings.find((finding) => finding.kind === kind)?.explanation, + ).toContain(`${VENDOR} loads only conditionally; if it loads,`); }); }); diff --git a/src/domain/apple/dylibResolutionFindings.ts b/src/domain/apple/dylibResolutionFindings.ts index baa6a72bc..d144a454c 100644 --- a/src/domain/apple/dylibResolutionFindings.ts +++ b/src/domain/apple/dylibResolutionFindings.ts @@ -61,24 +61,27 @@ const unresolvedFinding = (edge: Edge, index: number): Finding => { image: edge.loader, basis: "derived", } as const; + const missing = `No candidate for ${edge.command === "LC_LAZY_LOAD_DYLIB" ? "lazily loaded " : edge.weak ? "weak dependency " : ""}${edge.install_name} exists in the analyzed root`; + // Below a conditional fallback, every consequence applies only if it loads. + const when = edge.loader_conditional + ? `. ${edge.loader} loads only conditionally; if it loads, ` + : "; "; if (edge.command === "LC_LAZY_LOAD_DYLIB") return { ...base, kind: "lazy-load-unresolved", - explanation: `No candidate for lazily loaded ${edge.install_name} exists in the analyzed root; dyld loads it on first use, which would fail unless the image is supplied elsewhere.`, + explanation: `${missing}${when}dyld loads it on first use, which would fail unless the image is supplied elsewhere.`, }; return edge.weak ? { ...base, kind: "weak-load-unresolved", - explanation: `No candidate for weak dependency ${edge.install_name} exists in the analyzed root; dyld continues without it.`, + explanation: `${missing}${when}dyld continues without it.`, } : { ...base, kind: "required-load-unresolved", - explanation: edge.loader_conditional - ? `No candidate for ${edge.install_name} exists in the analyzed root. ${edge.loader} loads only conditionally; if it loads, dyld would fail to launch ${edge.root} (${edge.architecture}) unless the image is supplied elsewhere.` - : `No candidate for ${edge.install_name} exists in the analyzed root; dyld would fail to launch ${edge.root} (${edge.architecture}) unless the image is supplied elsewhere.`, + explanation: `${missing}${when}dyld would fail to launch ${edge.root} (${edge.architecture}) unless the image is supplied elsewhere.`, }; };