[PR #2292] [MERGED] fix(apple): limit already-loaded reuse to rpath dependencies #5144

Closed
opened 2026-10-31 01:04:19 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2292
Author: @viniciusdsandrade
Created: 10/12/2026
Status: ✅ Merged
Merged: 10/13/2026
Merged by: @morluto

Base: main ← Head: fix/apple-dylib-already-loaded-rpath


📝 Commits (2)

  • 8c064b6 fix(apple): limit already-loaded reuse to rpath dependencies
  • 4c95363 test(apple): verify loader_path dependencies across isolated directories

📊 Changes

4 files changed (+140 additions, -4 deletions)

View changed files

📝 docs/apple-application-analysis.md (+1 -1)
📝 src/domain/apple/dylibResolution.test.ts (+130 -0)
📝 src/domain/apple/dylibResolution.ts (+8 -2)
📝 src/domain/apple/dylibResolutionFindings.ts (+1 -1)

📄 Description

Summary

When multiple images in different directories depend on @loader_path/ libraries sharing an install name (for example, plugins or isolated helpers bundling @loader_path/libhelper.dylib), dyld dependency resolution checked context.byInstallName before candidate path expansion. Because byInstallName was populated for any install name, the second loader erroneously bound to the first loader's resolved copy or appeared resolved even when its own copy was absent.

Apple dyld (Loader.cpp:802-816) reuses already-loaded images by install name only for @rpath/ dependencies; caller-relative and literal dependencies are evaluated against their expanded candidate paths for each loader. Limit byInstallName registration and lookup to install names starting with @rpath/.

No existing issue covers this.

Problem and expected behavior

In src/domain/apple/dylibResolution.ts, resolveDependency consults context.byInstallName.get(dependency.install_name) before expanding candidate paths. When traceProcess loads an image whose slice has an install name, it registers context.byInstallName.set(slice.install_name, image).

Consider an application bundling two plugins in separate directories:

  • Contents/PlugIns/A/pluginA.dylib depending on @loader_path/libhelper.dylib
  • Contents/PlugIns/B/pluginB.dylib depending on @loader_path/libhelper.dylib

When pluginA.dylib loads, REA resolves @loader_path/libhelper.dylib to Contents/PlugIns/A/libhelper.dylib and registers @loader_path/libhelper.dylib -> Contents/PlugIns/A/libhelper.dylib in byInstallName. When pluginB.dylib is later evaluated, resolveDependency finds @loader_path/libhelper.dylib in byInstallName and immediately binds pluginB's dependency to Contents/PlugIns/A/libhelper.dylib without expanding candidate paths relative to pluginB's directory. If Contents/PlugIns/B/libhelper.dylib is absent, REA reports the dependency as resolved rather than emitting a required-load-unresolved finding.

Expected behavior: dyld reuses already-loaded images by install name only for @rpath/ dependencies. @loader_path/, @executable_path/, and literal dependencies expand candidate paths for each loader; identical images reached through identical paths are deduplicated once resolved.

Change and scope

  • In src/domain/apple/dylibResolution.ts: limit context.byInstallName lookup in resolveDependency to dependencies whose install_name starts with @rpath/. Limit context.byInstallName.set registration in traceProcess to slices whose install_name starts with @rpath/.
  • In docs/apple-application-analysis.md and src/domain/apple/dylibResolutionFindings.ts: update the documentation and limitation text to state that already-loaded reuse by install name applies to @rpath requests.
  • In src/domain/apple/dylibResolution.test.ts: add regression tests asserting that @loader_path dependencies across isolated directories do not reuse already-loaded images by install name, reporting required-load-unresolved when the second helper is absent and resolving to each loader's own directory when both exist.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Mach-O dependency resolution in src/domain/apple/dylibResolution.ts.
  • CLI and MCP/tool-catalog contract: unchanged schemas.
  • Provider, bridge, target-format, or platform compatibility: Apple dyld resolution semantics across macOS bundles.
  • Evidence, artifact, provenance, or reconstruction contract: trace_dylib_resolution candidate paths and resolution status reflect each loader's own directory for @loader_path/ dependencies.
  • Process execution, authorization, cleanup, or containment impact: none.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none.

Evidence and regression coverage

  • Tests added or updated: src/domain/apple/dylibResolution.test.ts adds tests under describe("loader-relative dylib resolution") verifying that @loader_path/ dependencies in isolated directories do not reuse already-loaded images by install name, both when the second loader's helper is absent and when each loader has its own helper.
  • Base reproduction or other evidence: on the base revision, pluginB's dependency resolves to Contents/PlugIns/A/libhelper.dylib even when Contents/PlugIns/B/libhelper.dylib is absent, and no required-load-unresolved finding is produced.
  • User-visible CLI/MCP output (if applicable): before, false-positive resolution of loader-relative dependencies across directories; after, correct candidate evaluation and required-load-unresolved finding when the target directory lacks the dependency.
  • Remaining proof gaps: tests were verified by source inspection; CI executes the test suite.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • No local commands were run: verification is source-derived; CI runs the deterministic suite.

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: none run.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no added process authority or external dependencies.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/2292 **Author:** [@viniciusdsandrade](https://github.com/viniciusdsandrade) **Created:** 10/12/2026 **Status:** ✅ Merged **Merged:** 10/13/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/apple-dylib-already-loaded-rpath` --- ### 📝 Commits (2) - [`8c064b6`](https://github.com/morluto/rea/commit/8c064b61ae3477a887eec51034403464536aba1f) fix(apple): limit already-loaded reuse to rpath dependencies - [`4c95363`](https://github.com/morluto/rea/commit/4c953638fd1ece90bfe0a3004f19e0b86066e5f1) test(apple): verify loader_path dependencies across isolated directories ### 📊 Changes **4 files changed** (+140 additions, -4 deletions) <details> <summary>View changed files</summary> 📝 `docs/apple-application-analysis.md` (+1 -1) 📝 `src/domain/apple/dylibResolution.test.ts` (+130 -0) 📝 `src/domain/apple/dylibResolution.ts` (+8 -2) 📝 `src/domain/apple/dylibResolutionFindings.ts` (+1 -1) </details> ### 📄 Description ## Summary When multiple images in different directories depend on `@loader_path/` libraries sharing an install name (for example, plugins or isolated helpers bundling `@loader_path/libhelper.dylib`), dyld dependency resolution checked `context.byInstallName` before candidate path expansion. Because `byInstallName` was populated for any install name, the second loader erroneously bound to the first loader's resolved copy or appeared resolved even when its own copy was absent. Apple dyld (`Loader.cpp:802-816`) reuses already-loaded images by install name only for `@rpath/` dependencies; caller-relative and literal dependencies are evaluated against their expanded candidate paths for each loader. Limit `byInstallName` registration and lookup to install names starting with `@rpath/`. No existing issue covers this. ## Problem and expected behavior In `src/domain/apple/dylibResolution.ts`, `resolveDependency` consults `context.byInstallName.get(dependency.install_name)` before expanding candidate paths. When `traceProcess` loads an image whose slice has an install name, it registers `context.byInstallName.set(slice.install_name, image)`. Consider an application bundling two plugins in separate directories: - `Contents/PlugIns/A/pluginA.dylib` depending on `@loader_path/libhelper.dylib` - `Contents/PlugIns/B/pluginB.dylib` depending on `@loader_path/libhelper.dylib` When `pluginA.dylib` loads, REA resolves `@loader_path/libhelper.dylib` to `Contents/PlugIns/A/libhelper.dylib` and registers `@loader_path/libhelper.dylib -> Contents/PlugIns/A/libhelper.dylib` in `byInstallName`. When `pluginB.dylib` is later evaluated, `resolveDependency` finds `@loader_path/libhelper.dylib` in `byInstallName` and immediately binds `pluginB`'s dependency to `Contents/PlugIns/A/libhelper.dylib` without expanding candidate paths relative to `pluginB`'s directory. If `Contents/PlugIns/B/libhelper.dylib` is absent, REA reports the dependency as resolved rather than emitting a `required-load-unresolved` finding. Expected behavior: dyld reuses already-loaded images by install name only for `@rpath/` dependencies. `@loader_path/`, `@executable_path/`, and literal dependencies expand candidate paths for each loader; identical images reached through identical paths are deduplicated once resolved. ## Change and scope - In `src/domain/apple/dylibResolution.ts`: limit `context.byInstallName` lookup in `resolveDependency` to dependencies whose `install_name` starts with `@rpath/`. Limit `context.byInstallName.set` registration in `traceProcess` to slices whose `install_name` starts with `@rpath/`. - In `docs/apple-application-analysis.md` and `src/domain/apple/dylibResolutionFindings.ts`: update the documentation and limitation text to state that already-loaded reuse by install name applies to `@rpath` requests. - In `src/domain/apple/dylibResolution.test.ts`: add regression tests asserting that `@loader_path` dependencies across isolated directories do not reuse already-loaded images by install name, reporting `required-load-unresolved` when the second helper is absent and resolving to each loader's own directory when both exist. ## Contract and boundary impact - Semantic owner and earliest changed stage: Mach-O dependency resolution in `src/domain/apple/dylibResolution.ts`. - CLI and MCP/tool-catalog contract: unchanged schemas. - Provider, bridge, target-format, or platform compatibility: Apple dyld resolution semantics across macOS bundles. - Evidence, artifact, provenance, or reconstruction contract: `trace_dylib_resolution` candidate paths and resolution status reflect each loader's own directory for `@loader_path/` dependencies. - Process execution, authorization, cleanup, or containment impact: none. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none. ## Evidence and regression coverage - Tests added or updated: `src/domain/apple/dylibResolution.test.ts` adds tests under `describe("loader-relative dylib resolution")` verifying that `@loader_path/` dependencies in isolated directories do not reuse already-loaded images by install name, both when the second loader's helper is absent and when each loader has its own helper. - Base reproduction or other evidence: on the base revision, `pluginB`'s dependency resolves to `Contents/PlugIns/A/libhelper.dylib` even when `Contents/PlugIns/B/libhelper.dylib` is absent, and no `required-load-unresolved` finding is produced. - User-visible CLI/MCP output (if applicable): before, false-positive resolution of loader-relative dependencies across directories; after, correct candidate evaluation and `required-load-unresolved` finding when the target directory lacks the dependency. - Remaining proof gaps: tests were verified by source inspection; CI executes the test suite. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - No local commands were run: verification is source-derived; CI runs the deterministic suite. ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: none run. - Package or release metadata impact: none. - Security, privacy, process, or containment review: no added process authority or external dependencies. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:04:19 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#5144
No description provided.