GHC/Build/Link: relativize absolute rpaths + self-$ORIGIN (forward-port onto master)#383
Closed
angerman wants to merge 3 commits into
Closed
GHC/Build/Link: relativize absolute rpaths + self-$ORIGIN (forward-port onto master)#383angerman wants to merge 3 commits into
angerman wants to merge 3 commits into
Conversation
depLibraryPaths returns absolute library paths whenever the dep's libdir is not under the package's own install prefix (i.e. almost always, in a cabal-store layout where each package gets its own hash-suffixed subdir). The previous getRPaths only prefixed `@loader_path` / `$ORIGIN` to already-relative paths, so those absolute store paths were baked directly into LC_RPATH (Mach-O) and DT_RUNPATH (ELF). On macOS up to and including Sonoma (macOS 14) dyld silently falls through to subsequent rpath entries when an absolute rpath does not exist. Sequoia (macOS 15) dyld treats the same condition as fatal and aborts the binary on launch. This affected the stable-haskell GHC bindist: a `/Volumes/WorkSpace/_work/ghc/ghc/ _build/stage2/store/host/aarch64-apple-darwin/lib` rpath baked in by the build runner caused `ghc --numeric-version` to SIGABRT when the bindist was installed on any macos-15 host. When the user has opted into relocatable mode (`--enable-relocatable`, or `relocatable: True` in a cabal project), this commit replaces such absolute rpaths with a `shortRelativePath`-computed relative form against the artifact's install directory. The relative form is well-formed (no `/Volumes` prefix to abort dyld on), and harmless when the bindist layout no longer matches the build store (dyld treats it as a normal missing rpath and falls through to subsequent entries). Non-relocatable builds (the default) are unchanged: absolute paths still pass through to preserve the existing semantics for non-relocated installs. The PackageDescription, InstallDirs, and shortRelativePath utility this needs are all already in scope or come from already-imported modules; only `bindir` and `libdir` field accessors are pulled in freshly from `Distribution.Simple.InstallDirs`. Companion to commit 010b365582c in stable-haskell/ghc, which strips the same leaked rpaths post-build with `install_name_tool` while this Cabal-side fix propagates through bindist rebuilds.
Initial version of this fix gated the new relativization on
`relocatable lbi` to keep non-relocatable builds byte-identical. The
gate doesn't help in practice:
* the stable-haskell GHC bindist build requires the new behavior
(else the darwin host binaries abort-trap on macOS 15);
* setting `relocatable: True` to flip the gate also triggers
`checkRelocatable` (which refuses cabal-store layouts whose deps
live in sibling prefixes) and changes how `library-dirs` are
written into .conf files — neither change is desirable for the
rpath fix, and the latter actively breaks the bindist
post-stage2 .conf rewriting in our Makefile pipeline.
The new always-on behavior is a strict improvement for every
relocatable scenario (cabal-store relocation, bindist relocation,
nix-style closure moves) and only a theoretical regression for the
"copy a single executable to an unrelated host and expect the same
absolute path to still resolve" case, which has never been part of
cabal's documented contract.
Companion to stable-haskell/ghc commit that drops the matching
`relocatable: True` flag from configure.ac.
…deps) The rpath relativization (added for the darwin LC_RPATH-leak fix) never adds the artifact's own directory to the runpath. depLibraryPaths can return the parent directory for a dependency installed in the same directory as the artifact, so that entry relativizes to "$ORIGIN/.." and the dependency is left unfound. glibc papers over this because GHC sets LD_LIBRARY_PATH at runtime before dlopen and glibc re-reads it per dlopen; musl reads LD_LIBRARY_PATH only at process startup and ignores the runtime change, so the missing self-rpath is fatal there. Concretely, a Backpack signature implementation (libHSp) installed next to the instantiated unit (libHSindef) fails to load under musl (GHC testsuite backpack/cabal/T14304 on Alpine), though it passes on every glibc platform. Always prepend the loader-relative self directory ($ORIGIN, or @loader_path on macOS) to the rpaths so a same-directory sibling is found. This is a relative entry, so it does not reintroduce the absolute build-host path the relativization removed (no macOS regression).
7 tasks
Author
|
Part of the reconciliation tracked in #384. |
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft / forward-port for review. Brings the absolute-rpath relativization
to post-split
stable-haskell/masterand adds a self-$ORIGINrpath entry.What
Three commits onto
stable-haskell/master:GHC/Build/Link: relativize absolute rpaths when --enable-relocatableGHC/Build/Link: drop the relocatable gate, always relativize abs rpathsGHC/Build/Link: include $ORIGIN/@loader_path in rpaths(1)+(2) are #368 (
feat/rpath-relativize-absolute), which currently targets thepre-split
feature/rebase-CI; this is the same change rebased ontopost-split
master, wheregetRPathsstill bakes absolute build-host pathsinto
LC_RPATH/DT_RUNPATH.(3) is new:
getRPathsnever adds the artifact's own directory to therunpath. For a dependency that ends up in the same directory (a Backpack
signature impl
libHSpnext to the instantiatedlibHSindef),depLibraryPathsyields the parent and it relativizes to$ORIGIN/..— so thedep isn't found. glibc papers over it via the runtime
LD_LIBRARY_PATHGHCsets before
dlopen; musl readsLD_LIBRARY_PATHonly at startup, so it'sfatal there (GHC testsuite
backpack/cabal/T14304on Alpine/musl; passes onevery glibc platform). Prepending the loader-relative self dir is relative, so
it does not reintroduce the absolute path (1)+(2) remove (no macOS regression).
Why here
This is part of reconciling the cross/dual-compiler stack onto
master. Thefixes the stable-haskell GHC build needs (#361 tool-guess, #368 rpath, this
$ORIGINfollow-up, and the stage-qualified/host-only work in #378) are spreadacross pre- and post-split feature branches;
masterhas none of the rpathwork.
stable-haskell/ghccurrently has to pin the pre-splitfeature/wasm-cross-ghcup-stackto get these, which forces it to also revertGHC's
Setup.hsto the pre-VerbosityHandlesAPI and drophooks-exe. Landingthe rpath stack on
master(alongside #361 and #378) removes that need.Caveat
Cherry-picked cleanly (no conflicts) but not built locally — please let CI
validate, particularly that the relativization still typechecks against the
post-split
LocalBuildInfo/InstallDirsAPI (absoluteComponentInstallDirs,NoCopyDest,bindir/libdir). Filing as draft for @andreabedini's review;could alternatively stack on
wip/andrea/stage-qualified-options.