Skip to content

Use ghc --merge-objs when available to join object files - #9226

Open
bgamari wants to merge 1 commit into
haskell:masterfrom
bgamari:wip/merge-objs
Open

bgamari wants to merge 1 commit into
haskell:masterfrom
bgamari:wip/merge-objs

Conversation

@bgamari

@bgamari bgamari commented Sep 1, 2023

Copy link
Copy Markdown
Contributor

Previously Cabal would rely on its own object-merge logic to produce
GHCi libraries. This was not ideal as it meant that this logic was
replicated in both GHC and Cabal. Moreover, Cabal's implementation
historically hasn't handled the more subtle aspects of interacting with
the linker, resulting in issues like #7828.

In GHC 9.4 we have introduced a new compiler mode, --merge-objs, which
exposes GHC's object merging mechanism. Here we teach Cabal to take
advantage of this mechanism when it is available. This ensures that the
platform-dependent process of object merging remains squarely in GHC's
court, preventing issues like #7828.

This is a rebase of #7960, which we previously decided not to merge as GHC
was able to find another way around the proximate problem. However, in light
of recent issues turned up by GHC's ghc-toolchain work, I think we should again
consider merging this for GHC 9.10.

@bgamari bgamari changed the title Wip/merge objs Use ghc --merge-objs when available to join object files Sep 1, 2023
@bgamari
bgamari force-pushed the wip/merge-objs branch 4 times, most recently from 92bb632 to 247d563 Compare September 1, 2023 14:15
Previously Cabal would rely on its own object-merge logic to produce
GHCi libraries. This was not ideal as it meant that this logic was
replicated in both GHC and Cabal. Moreover, Cabal's implementation
historically hasn't handled the more subtle aspects of interacting with
the linker, resulting in issues like haskell#7828.

In GHC 7.4 we have introduced a new compiler mode, `--merge-objs`, which
exposes GHC's object merging mechanism. Here we teach Cabal to take
advantage of this mechanism when it is available.
@erikd

erikd commented Sep 6, 2023

Copy link
Copy Markdown
Member

@bgamari I am currently working on something similar. How does your solution work with a version of GHC, < 9.4 ?

From reading the code, cabal would pass --merge-objs even for older compilers. Please correct me if I am wrong.

I ask because in CI jobs it is common for people to run a bunch of compilers with a single (latest) version of cabal. Using this new version of cabal will likely break a lot of CI builds.

@andreabedini

andreabedini commented Sep 6, 2023

Copy link
Copy Markdown
Collaborator

For context: recently we also had #9220 which wanted to restore being able to use --enable-library-for-ghci on Windows when the linker allows it (i.e. support object merging), which was disabled in #8071.

That PR wouldn't fly by itself but #9229 added the appropriate feature detection to ldProgram and @erikd is now working (#9236) to enable --enable-library-for-ghci based on that linker feature rather than the existing check based on the GHC version and operating system.

I admit my ignorance on the topic but I am bit confused. #8071 was submitted to support GHC 9.4's new Windows toolchain, but it disabled ghci libs because lld does not support merging object files. Reading this PR, I understand that GHC 9.4 had also gained the ability to merge object files. Delegating linking to GHC is exactly what @angerman was suggesting in #9220.

Can ghc merge object files on Windows too? I feel we need a coherent picture.

@angerman

angerman commented Sep 6, 2023

Copy link
Copy Markdown
Collaborator

As @andreabedini highlighted, we do need merged objects for the linker performance on windows. We also do not use lld, even though upstream now uses it. As such we can still rely on bfd.ld to be able to merge objects. This is the core of the pull request @erikd made in #9226. If cabal can shell out to ghc, that would be even better.
@bgamari does GHC's --merge-objs flag work conditional on the configured linker, or does it outright not merge objects on windows (as #8071 did to cabal?).

@bgamari

bgamari commented Sep 6, 2023

Copy link
Copy Markdown
Contributor Author

I admit my ignorance on the topic but I am bit confused. #8071 was submitted to support GHC 9.4's new Windows toolchain, but it disabled ghci libs because lld does not support merging object files. Reading this PR, I understand that GHC 9.4 had also gained the ability to merge object files. Delegating linking to GHC is exactly what @angerman was suggesting in #9220.

In Haskell we use object merging for two different purposes:

  1. In GHC: To merge C stubs into a module's object during compilation. Here we don't need ld -r specifically, but rather any means of merging multiple object files into a single linkable
  2. In Cabal and Hadrian: To build merged "GHCi objects". Here we use ld -r to eliminate split sections overhead, greatly improving load times in GHCi

Since GHC requires (1), we implemented object merging on Windows by simply building a static archive of the merged objects. This can be passed to the linker just as a merged object resulting from ld -r could. However, this implementation offers no performance benefit to GHCi. For this reason, we no longer enable GHCi object support in Cabal on platforms which lack ld -r; building such objects would imply doing work that offers no benefit.

Furthermore, even if we had the ability to use ld -r, GHCi objects should be much less necessary on Windows than other platforms as we do not support split sections on Windows.

Can ghc merge object files on Windows too? I feel we need a coherent picture.

GHC's merge-object mode will work on any platform for which the toolchain supports object merging. However, LLD on Windows (which is the only configuration we can support) does not support object merging. I would strongly recommend against using ld.bfd at all on Windows (even merely for object merging) since:

  • I know of at least one bug which results in incorrect object code
  • Binutils is, as far as I know, still unable to correctly generate relocatable objects and therefore may break ASLR, which is now default and may soon be mandatory on Windows platforms
  • linking performance is quite poor
  • binutils does not support long filepaths on Windows, making Haskell builds quite likely to fail

However, I think @angerman and I agree that object merging logic should be in GHC, not Cabal, and I would be fine with allowing Cabal to use --merge-objs even on Windows, assuming that:

  • we don't enable GHCi objects by default since this would perform unnecessary work for the majority of Windows users (since they will be using official GHC bindists and therefore lld)
  • we teach GHC to warn if --merge-objs is used with a compiler which doesn't support object merging (as proposed in https://github.com/haskell/cabal/pull/9220/files#r1310987386)

@angerman

angerman commented Sep 6, 2023

Copy link
Copy Markdown
Collaborator

@bgamari I still disagree that

  • cabal should not invoke merge objects for windows targets if explicitly asked to; if --enable-ghci-library is passed it should do so. Whether or not that flag is default on or off for that target, I'm not very strong on. But cabal should obey the request.
  • GHC should not flat out refuse to do merge objects. It should so so where it knows it can't do so (feature detect -r of the linker).

From my observations, loading pre-linked object files into the linker is significantly faster than loading the archives. If we had proper dynamic lining on windows this would look quite differently.

@angerman

angerman commented Sep 6, 2023

Copy link
Copy Markdown
Collaborator

@bgamari can you share a link for

  • I know of at least one bug which results in incorrect object code

Also of note: as usual, I'm primarily focused on cross compilation, not that much on compilation on windows; but for.

@andreabedini

Copy link
Copy Markdown
Collaborator

Also in light of #9301, do we have a consensus on how to proceed on this?

In general terms I belive cabal should ask GHC do to what the user wants to do and it is up to GHC to warn or error when it cannot do what it is asked.

@Mikolaj Mikolaj added cross-compilation re: dynamic-linking Concerning dynamic linking (e.g. flags "shared", "*-dynamic") re: ghc-options Concerning passing options to GHC labels Oct 3, 2023
@bgamari

bgamari commented Oct 3, 2023

Copy link
Copy Markdown
Contributor Author

@angerman, one example is binutils #26757 but I believe there are others as well.

@angerman

angerman commented Oct 3, 2023

Copy link
Copy Markdown
Collaborator

@bgamari which only happens for >4GB images? MSYS2 mingw64 and even ucrt64 environments still use the gnu toolchain. Only the clang64 env uses the llvm toolchain. So msys still seems to consider the gcc toolchain to be good enough.

In any case, my view is that these features should be gated by feature availability, and not by proxy.

If we have a gcc toolchain GHC we can have merge objects. If not, we can't. And as it stands we can still build GHC with a different toolchain even if that's not the one we officially ship with the binary distributions.

Therefor if $ld supports -r, we should support --merge-objects if the user requests.

@bgamari

bgamari commented Oct 3, 2023

Copy link
Copy Markdown
Contributor Author

@bgamari which only happens for >4GB images?

No, it occurs for images mapped above >4GB, which is now the default.

In any case, my view is that these features should be gated by feature availability, and not by proxy.

Sure, I don't disagree. I would simply advise caution relying on the gnu toolchain for PE targets. There are good reasons why GHC moved away from it.

@andreabedini

Copy link
Copy Markdown
Collaborator

Cross-reference: #12333 is fixing the ld lookup for GHC >= 9.10 (ld command is no longer in the settings). Every GHC affected has --merge-objs, so I've suggested there that we revive this PR instead. @bgamari would you be up for a rebase, or happy for someone else to pick it up? I don't think the Windows question blocks it: GHC falls back to ar when no merge tool is configured and Cabal disables GHCi libs there anyway.

andreabedini added a commit to andreabedini/cabal that referenced this pull request Sep 15, 2026
Cabal used to run ld -r itself to merge a library's object files into the
GHCi object, finding ld from the ghc settings and probing it for -r and -x
support. When ghc supports --merge-objs, delegate the merging to it: GHC
uses the merge tool and flags it was configured with, deals with long
argument lists, and its choice is by construction the one GHCi can load.

Whether a GHCi library can be built at all is then decided by whether GHC
has a merge tool configured ("Merge objects command" in the settings)
instead of by probing ld --help. The ld -r path is kept for GHCs without
--merge-objs.

This revives haskell#9226 with feature detection instead of a version gate.
Refs haskell#7828, haskell#9301, haskell#12332.
andreabedini added a commit to andreabedini/cabal that referenced this pull request Sep 15, 2026
We used to run `ld -r` ourselves, finding ld from the ghc settings and
probing it for -r and -x. When ghc has --merge-objs, let it do the
merging: it uses the tool and flags it was configured with and handles
long argument lists. Whether a GHCi library can be built at all then
follows from "Merge objects command" being set, not from probing
`ld --help`. GHCs without --merge-objs keep the ld path.

Revives haskell#9226 with feature detection instead of a version gate.
Refs haskell#7828, haskell#9301, haskell#12332.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
andreabedini added a commit to andreabedini/cabal that referenced this pull request Sep 15, 2026
We used to run `ld -r` ourselves, finding ld from the ghc settings and
probing it for -r and -x. When ghc has --merge-objs, let it do the
merging: it uses the tool and flags it was configured with and handles
long argument lists. Whether a GHCi library can be built at all then
follows from "Merge objects command" being set, not from probing
`ld --help`. GHCs without --merge-objs keep the ld path.

Revives haskell#9226 with feature detection instead of a version gate.
Refs haskell#7828, haskell#9301, haskell#12332.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@andreabedini

Copy link
Copy Markdown
Collaborator

I revived this PR as #12358

andreabedini added a commit to andreabedini/cabal that referenced this pull request Sep 17, 2026
We used to run `ld -r` ourselves, finding ld from the ghc settings and
probing it for -r and -x. When ghc has --merge-objs, let it do the
merging: it uses the tool and flags it was configured with and handles
long argument lists. Whether a GHCi library can be built at all then
follows from "Merge objects command" being set, not from probing
`ld --help`. GHCs without --merge-objs keep the ld path.

Revives haskell#9226 with feature detection instead of a version gate.
Refs haskell#7828, haskell#9301, haskell#12332.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
andreabedini added a commit to andreabedini/cabal that referenced this pull request Sep 21, 2026
We used to run `ld -r` ourselves, finding ld from the ghc settings and
probing it for -r and -x. When ghc has --merge-objs, let it do the
merging: it uses the tool and flags it was configured with and handles
long argument lists. Whether a GHCi library can be built at all then
follows from "Merge objects command" being set, not from probing
`ld --help`. GHCs without --merge-objs keep the ld path.

Revives haskell#9226 with feature detection instead of a version gate.
Refs haskell#7828, haskell#9301, haskell#12332.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cross-compilation re: dynamic-linking Concerning dynamic linking (e.g. flags "shared", "*-dynamic") re: ghc-options Concerning passing options to GHC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants