I would like to propose adding support for multi-architecture object files that contain both ARM64EC and AArch64 object files as the result of compilation with -marm64x. This is an alternative to [RFC] Multi-architecture COFF object files for ARM64X that does not need an archive format change from [RFC] Introduce a -wholearchive marker embedded in archives themselves.
Together with ARM64EC, Windows on ARM introduced ARM64X image files, which are binaries containing both ARM64EC and AArch64 code. In MSVC, this is implemented entirely as a linker feature. Users are expected to compile sources separately for both targets and then pass both sets of object files to the linker ( Build Arm64X Files | Microsoft Learn ). We already support this workflow in both Clang and LLD. The build process typically looks like this:
clang -target aarch64-windows -c foo.c -o foo-aarch64.o
clang -target arm64ec-windows -c foo.c -o foo-arm64ec.o
clang -marm64x -o bar.exe foo-aarch64.o foo-arm64ec.o
This differs significantly from a typical build process. The requirement to produce two object files for every source file is challenging for build systems such as CMake, autoconf or Meson. I believe this can be significantly simplified by moving more of the logic into the toolchain itself. Ideally, the above example could instead become:
clang -marm64x -c foo.c -o foo.o
clang -marm64x -o bar.exe foo.o
This is straightforward for build systems to understand and is effectively equivalent to CFLAGS=-marm64x LDFLAGS=-marm64x.
This requires foo.o to contain both AArch64 and ARM64EC object files. The concept is not entirely new; Mach-O has supported multi-architecture binaries for years. With Mach-O, users typically have two options: build separate images and merge them afterward or build each source file into a multi-architecture object file. The former approach is not feasible for ARM64X due to format constraints, so I would like to provide an experience similar to Mach-O multi-architecture object files.
I’d like to introduce a new .llvm.arm64x section, similar to how .llvm.lto is used for embedding bitcode object files. This allows aware tools to leverage the additional capability while ensuring unaware tools simply ignore the section.
On the Clang side, this is achieved by compiling separately for both the ARM64 and ARM64EC targets and merging the results via an additional llvm-objcopy job. lld-link then selects the correct view of the file depending on the target architecture. Additionally, the archive writer splits the object file into separate members so that they are properly reflected in the symbol map. An advantage of such treatment in archives is that the resulting static library can be used seamlessly by the MSVC linker, as it no longer relies on the .llvm.arm64x section.
PR with a draft implementation: [COFF][ARM64X] Multi-architecture COFF object files for ARM64X by cjacek · Pull Request #202740 · llvm/llvm-project · GitHub