Skip to content

Link LLD's COFF, ELF and Mach-O linkers into talac - #116

Merged
Leitwolf11 merged 12 commits into
mainfrom
feature/in-process-lld
Oct 31, 2026
Merged

Leitwolf11 merged 12 commits into
mainfrom
feature/in-process-lld

Conversation

@Leitwolf11

Copy link
Copy Markdown
Contributor

Link LLD's COFF, ELF and Mach-O linkers into talac

talac started lld-link, cc or clang as separate programs to link an
executable, and wrote the import libraries of a Windows executable by
running lld-link /lib. It now links lldCOFF, lldELF, lldMachO and
lldCommon statically and runs them in its own process.

  • A Windows executable is linked by LLD's lld-link with the plan's
    arguments and needs nothing from the host; its import libraries
    (kernel32 for TalaRT's archive, talart.dll for the shared library) are
    written by llvm::object::writeImportLibrary.
  • A Linux, macOS or BSD executable needs the start-up objects and the C
    library of its target, which a C compiler knows the places of. talac
    runs clang -### with the plan's arguments, which prints the command
    line of the linker without running anything, and runs that command line
    with LLD's ELF or Mach-O linker. A cross target asks clang with
    --target; a host target is given its triple too. When no clang is on
    PATH, a host target is linked by cc and its own linker as before.
  • LinkerCommandOf reads the linker's command line from the driver's
    output (quoted words, backslash escapes, the last command whose program
    is a linker); LinkPlanTests read clang output of Linux, macOS and a
    Windows host.

Building: llvm.bzl finds LLD's libraries and headers beside LLVM or in
LLD_ROOT (brew --prefix lld on macOS), adds the LLVM components LLD
uses (all targets, LTO, DTLTO where LLVM has it, debug info, option,
TextAPI, the Windows drivers and manifest), and exposes @llvm//:lld with
the libraries in link order. LLVM's release for Windows names zlib, zstd
and libxml2 among its system libraries without shipping them, so the
Windows build takes them from the Bazel Central Registry. The macOS job
installs the lld formula. The Linux hosts need the libc++ toolchain with
LLD (#115) pinned before this can build there.

LldTests, a new native suite, write import libraries for x64 and arm64,
check that a refused link reports LLD's messages and that LLD links again
afterwards, and link a Windows executable from an object LLVM generates in
the test; on a Windows host the executable runs and returns its entry's
42. Locally on Windows: lld_tests 3/12, link_plan_tests 9/103,
verify-abi and the Go helper tests pass.

Leitwolf11 and others added 11 commits October 30, 2026 12:41
talac started lld-link, cc or clang as separate programs to link an
executable, and wrote the import libraries of a Windows executable by
running lld-link /lib. It now links lldCOFF, lldELF, lldMachO and
lldCommon statically and runs them in its own process.

- A Windows executable is linked by LLD's lld-link with the plan's
  arguments and needs nothing from the host; its import libraries
  (kernel32 for TalaRT's archive, talart.dll for the shared library) are
  written by llvm::object::writeImportLibrary.
- A Linux, macOS or BSD executable needs the start-up objects and the C
  library of its target, which a C compiler knows the places of. talac
  runs `clang -###` with the plan's arguments, which prints the command
  line of the linker without running anything, and runs that command line
  with LLD's ELF or Mach-O linker. A cross target asks clang with
  --target; a host target is given its triple too. When no clang is on
  PATH, a host target is linked by cc and its own linker as before.
- LinkerCommandOf reads the linker's command line from the driver's
  output (quoted words, backslash escapes, the last command whose program
  is a linker); LinkPlanTests read clang output of Linux, macOS and a
  Windows host.

Building: llvm.bzl finds LLD's libraries and headers beside LLVM or in
LLD_ROOT (`brew --prefix lld` on macOS), adds the LLVM components LLD
uses (all targets, LTO, DTLTO where LLVM has it, debug info, option,
TextAPI, the Windows drivers and manifest), and exposes @llvm//:lld with
the libraries in link order. LLVM's release for Windows names zlib, zstd
and libxml2 among its system libraries without shipping them, so the
Windows build takes them from the Bazel Central Registry. The macOS job
installs the lld formula. The Linux hosts need the libc++ toolchain with
LLD (#115) pinned before this can build there.

LldTests, a new native suite, write import libraries for x64 and arm64,
check that a refused link reports LLD's messages and that LLD links again
afterwards, and link a Windows executable from an object LLVM generates in
the test; on a Windows host the executable runs and returns its entry's
42. Locally on Windows: lld_tests 3/12, link_plan_tests 9/103,
verify-abi and the Go helper tests pass.
Homebrew's lld formula builds LLD with BUILD_SHARED_LIBS=ON: its
libraries are dylibs over libLLVM.dylib, and linking them would put a
second copy of LLVM into talac beside the static one. The macOS job now
builds lldCommon, lldCOFF, lldELF and lldMachO as static libraries from
the source release of the same LLVM version, against Homebrew's LLVM,
the way the formula builds LLD, caches them per version and runner, and
names them through LLD_ROOT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LinkerCommandOf took the name of the program with std::filesystem, which
does not treat a backslash as a separator on POSIX, so the parser depended
on the host and failed on macOS for the test that reads the output of a
Windows host. It now cuts at either separator and drops .exe itself.
Revision 2 of the Linux toolchain carries LLD's static libraries, which
talac now links, and is published from main. The helpers and both
Containerfiles take its archives by their digests.
The branch already carries the cross-linking work that main received. Its
changelog entry named lld-link and cc, which the linkers inside talac have
replaced, so the entry keeps what still holds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
std::rand is not thread safe, which clang-tidy refuses, and without a seed
it names the same directory in every run. LLVM makes one that is unique.
@Leitwolf11

Copy link
Copy Markdown
Contributor Author

Self-review: LLD's COFF, ELF and Mach-O linkers are linked into talac statically, and Lld.cpp is the only place that calls them, through lld::lldMain. Import libraries are written by LLVM's writeImportLibrary. A Windows target needs nothing from the host. ELF and Mach-O take the linker line from clang -###, read by LinkerCommandOf, which is pure and independent of the host's path rules, and run it in-process; without clang a target of the host falls back to cc. The build takes LLD's static libraries from the LLVM release on Windows, from toolchain revision 2 on Linux, and from a cached static build against Homebrew's LLVM on macOS, because Homebrew's lld is shared over libLLVM. Linux digests are pinned in the helpers and both Containerfiles. Known limits: the cross path still passes -fuse-ld=lld to clang -###, which may fail where no ld.lld is installed, and runtimes/<triple>/ is not staged. CI 40/40 green.

@Leitwolf11
Leitwolf11 merged commit 3ebc2cb into main Oct 31, 2026
43 checks passed
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