ld64.lld in the Swift Linux toolchain is incredibly useful for cross-compiling from Linux to iOS/macOS — for example, it makes it so that the Swift Darwin SDK need only ship with two files in its toolset: dsymutil and llvm-libtool-darwin — we don't need to include a linker in the SDK since ld64.lld already ships with the OSS Swift toolchain.
Unfortunately, lld's ld64 operation mode on Linux is broken in recent Swift 6.1 snapshots. The linker bails with the error
This version of lld does not support linking for platform iOS
This is due to a recent patch to Swift's downstream LLVM fork that explicitly makes ld64.lld emit this error message. Going off the details in the PR, the patch was intended to disable lld's Mach-O functionality on macOS hosts and steer folks towards /usr/bin/ld instead. Unfortunately, Linux got caught in the crossfire and it is now also unable to run ld64.lld despite 1) not having a /usr/bin/ld to fall back on and 2) lld fully supporting the ability to build Mach-O binaries, artificial error message notwithstanding.
--- a/lld/MachO/InputFiles.cpp
+++ b/lld/MachO/InputFiles.cpp
@@ -157,6 +157,11 @@ static bool checkCompatibility(const InputFile *input) {
if (platformInfos.empty())
return true;
+ // Swift LLVM fork downstream change start
+ error("This version of lld does not support linking for platform " + getPlatformName(platformInfos.front().target.Platform));
+ return false;
+ // Swift LLVM fork downstream change end
+
auto it = find_if(platformInfos, [&](const PlatformInfo &info) {
return removeSimulator(info.target.Platform) ==
removeSimulator(config->platform());
I put up a small PR to make it so the message on Linux is a warning rather than an error. I was asked by @rauhul to request the Platform Steering Group for their thoughts on this, hence the forum post. cc @platform-steering-group (but also if anyone else has thoughts/context/opinions, please do engage!)
tl;dr: ld64.lld can build Darwin binaries on Linux out of the box, but due to a change that was aimed to disable it on macOS, it is now also broken on Linux in Swift 6.1. Was asked to gather feedback from the Platform Steering Group on my PR to re-enable it.
Disclaimer: To be clear, I don't think the responsibility for limiting lld rests on anyone involved in the original PR. I assume this was an upstream decision which is why I'm now being requested to gather feedback from the Platform Server Group.
Assuming the change works as intended, isn't the question presented whether this use case is supported/supportable long-term? It seems like the happy path (everything seems to be working) could become very unhappy if lots of people grow to depend on it, and then find that some necessary change can't be integrated in any reasonable time frame. As an OS developer, I'd feel obligated to avoid undue reliance by providing clear boundaries, like an error.
As a contributor seeking to expand (or preserve) the use-case, I'd have more credibility if I contributed the validation necessary to ensure the happy path stays happy (over all supported combinations of Linux+Darwin?). That seems like a fair bit more work than a small patch reverting the change to a warning. It would be hard to undertake all that work without clear signals that one's approach would be acceptable. I'm not even sure here how that signal could be negotiated, given the uncertainties involved (esp. since linking is evolving). It's a really tough position to be in.
(As a side note, I don't think amplifications like "artificially restricts" and "fully supporting" help the discussion to the extent they re-shape the consideration that's due. It would be better to enumerate and evaluate the possible issues clearly to stage the discussion about risks and work involved. That in itself is a lot of work; as an OS developer I sometimes default to no because, well, "there be dragons!", and I don't particularly want to spend time investigating difficulties when I have pressing needs.)
Apologies if some of the terminology I used came off as hyperbolic. I agree that I didn't sufficiently qualify those phrases but I do think they're justified. To put things more concretely, see these slides on the topic from EuroLLVM 2022. To quote/paraphrase the talk, large-scale adopters include:
Meta: Used for development builds of iOS apps
Lyft: Used for development builds of iOS apps
Chrome: uses it in production(!) YouTube and Maps are about to roll it out for
internal development builds
(EDIT: I've also adjusted the post title to be more descriptive. Thanks for the feedback.)
LLD already has a test suite that includes tests for its Mach-O linker. Those tests were also (necessarily) disabled as a followup to the aforementioned PR but can be re-enabled on Linux if the error there is downgraded to a warning.
All things considered I agree that cross-compilation from Linux to Darwin is a use case that isn't battle tested to the same degree as Darwin-to-Darwin cross, but there's plenty of precedent for the Swift toolchain including experimental functionality. On the more user-facing side, there most evident ones are enableExperimentalFeature and the various flags under --help-hidden. But moreover Swift and Clang themselves have long supported the ability to cross-compile from Linux to Darwin and I'd venture to say that supporting it with LLD involves the same tradeoffs.
To make sure we're on the same page here: I agree that it's reasonable to disable ld64.lld in macOS distributions of the Swift toolchain; it was never included until Swift 6.1, and is intended to be used for embedded/Wasm SDKs on macOS from Swift 6.1 onwards. Meanwhile, Linux toolchains for Swift have shipped with LLD for a while (since at least 6.0, but possibly earlier, I haven't checked). IMHO it's reasonable to continue shipping ld64.lld on Swift 6.1, independent of the changes made for macOS hosts.
With that said, I do think your point about maintenance burden is very valid. If keeping ld64.lld in compiling + tests-passing state has involved friction in the past, I completely understand disabling it. I would love to understand whether that has or has not been the case.
One major issue with cross-compilation is if you plan to use any macros in the SDK frameworks, like Foundation, SwiftData, SwiftUI, and so forth. Those are distributed as prebuilt .dylibs in the toolchain and there is no source distribution for them. That makes cross-compilation on any platform other than Darwin impossible (unless you simply restrict yourself to a subset of the APIs).
There are other oddities as well, like framework .swiftinterface files referencing @_semantics to control codegen that are not found in the open-source toolchain. It's unclear to me whether any of those are mandatory.
The cross-linking issues you're referring to are more tractable in comparison, but unless the distribution of macros were to change, I don't think cross-compilation is feasible at all.
It's definitely unfortunate that SDK-specific macros don't work cross-platform atm, but this is something that folks including @Max_Desiatov and myself are working to alleviate with WebAssembly macros. I can't speak to whether Apple will rebuild their all-in-one-plugin-server-based macros with WebAssembly but the use cases extend to functionality like hermeticity and more restrictive sandboxing so I certainly hope so.
It makes me really happy to say that aside from macros, cross-compilation from Linux to Darwin is completely feasible and actually a really nice experience
You can give it a spin yourself today: just install my the Darwin SDK from here and build your SwiftUI app in a SwiftPM executableTarget! If you build for macOS you can just copy the executable over and run it, and on iOS you can bundle it into a .app, sign, and install. (Aside: still figuring out whether I can distribute this as a package or whether I should require people to download Xcode.xip themselves)
I'd also recommend checking out my previous post on the topic here (things have come a long way since then; the DX is a lot nicer now):
That's the clincher, I think. When I referred to it not being "feasible", there's technical feasibility and logistical feasibility, and this is more the latter. It's one thing to codegen for a different target platform, but if the APIs people want to use aren't there because of other design reasons, there's simply no good workaround. We can't ask our users to hold themselves to just the subset of the language that works.
This is not to diminish your work in any way—it's impressive and I'm glad you're pushing forward on this space and making sure things work well regardless of the host platform the toolchain is running on!