Skip to content

mkStandaloneApp: fail on a bundled dylib that loads outside the closure - #169

Draft
osmaczko wants to merge 1 commit into
masterfrom
fix/darwin-bundle-dylib-closure
Draft

mkStandaloneApp: fail on a bundled dylib that loads outside the closure#169
osmaczko wants to merge 1 commit into
masterfrom
fix/darwin-bundle-dylib-closure

Conversation

@osmaczko

@osmaczko osmaczko commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

An app can build, cache and run while a module it ships cannot load at all. Modules reach an app inside LGX archives, and a tar is opaque to nix's reference scanning, so an absolute store path baked into a bundled Mach-O is never registered as a reference of anything: nix does not install it alongside the app, and dlopen fails wherever that path does not already exist. Such a path reaches a machine only as a side effect of building the module that names it, so the same unchanged module passes while CI still compiles it and starts failing once the binary cache serves it prebuilt, with nothing in any diff to point at. Linux has no equivalent hole, because autoPatchelfHook resolves and validates every DT_NEEDED at build time.

This shipped, and cost a bisect over source revisions that could never have converged: a delivery module's librln named a libiconv nothing installed, and the missing module surfaced downstream as an intermittent crash in the module that waited on it.

Building a standalone app now fails when a Mach-O it ships loads a store path outside the closure the app realises, naming both the file and the path it wants. The check reads LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB and LC_REEXPORT_DYLIB and compares them against closureInfo over exactly what nix run puts on the machine, so it asks the question the loader will ask.

Testing it needs a macOS runner, and install-nix-action v27 cannot install there: its bundled installer aborts on the nixbld records the runner image already carries. Both jobs move to v31 so the repo keeps one installer at one pin.

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

📊 Doc-test reports

The four ways to wrap a C library plus the two cross-language composition tours — each scaffolded into real modules, built against this commit, loaded in logoscore, and driven — rendered alongside the commands actually run and their output (updated each run, commit 719869a):

Pages can take a minute to update after the run finishes.

@osmaczko
osmaczko force-pushed the fix/darwin-bundle-dylib-closure branch 4 times, most recently from 5a2b06d to 01270c6 Compare July 29, 2026 13:37
An app can build, cache and run while a module it ships cannot load at all. Modules reach an app inside LGX archives, and a tar is opaque to nix's reference scanning, so an absolute store path baked into a bundled Mach-O is never registered as a reference of anything: nix does not install it alongside the app, and dlopen fails wherever that path does not already exist. Such a path reaches a machine only as a side effect of building the module that names it, so the same unchanged module passes while CI still compiles it and starts failing once the binary cache serves it prebuilt, with nothing in any diff to point at. Linux has no equivalent hole, because autoPatchelfHook resolves and validates every DT_NEEDED at build time.

This shipped, and cost a bisect over source revisions that could never have converged: a delivery module's librln named a libiconv nothing installed, and the missing module surfaced downstream as an intermittent crash in the module that waited on it.

Building a standalone app now fails when a Mach-O it ships loads a store path outside the closure the app realises, naming both the file and the path it wants. The check reads LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB and LC_REEXPORT_DYLIB and compares them against closureInfo over exactly what `nix run` puts on the machine, so it asks the question the loader will ask.

Testing it needs a macOS runner, and install-nix-action v27 cannot install there: its bundled installer aborts on the nixbld records the runner image already carries. Both jobs move to v31 so the repo keeps one installer at one pin.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant