Cross-Compiling Chromium for ARM64 on x86_64 Linux Hosts

This article provides a quick overview of how the Chromium build system cross-compiles ARM64 binaries on an x86_64 Linux host machine. It covers how Chromium leverages a hermetic LLVM/Clang toolchain, isolated target sysroots, the GN meta-build system, and an integrated CPU simulator to compile complex target binaries while simultaneously building and running necessary host utilities.

1. The Hermetic Clang Toolchain

Chromium does not rely on the host system’s default compiler (such as a locally installed GCC). Instead, during the initial source checkout, Chromium's gclient tool downloads a pre-built, hermetic version of the LLVM toolchain (Clang and the LLD linker) that runs natively on x86_64.

Because Clang is inherently a cross-compiler by design, Chromium does not need a separate toolchain binary to build for ARM64. The exact same x86_64 Clang binary emits ARM64 machine code simply by receiving the --target=aarch64-linux-gnu flag during the build process.

2. Managing Target Dependencies with Sysroots

Compiling software for ARM64 requires ARM64-specific system headers and shared libraries (such as glibc, libstdc++, GTK, and X11). An x86_64 host machine only possesses x86_64 versions of these files in its /usr/include and /usr/lib directories.

Chromium solves this by using a sysroot—a directory containing a minimal root filesystem of an ARM64 Linux distribution (typically Debian). During the gclient runhooks step, Chromium executes a script (build/linux/sysroot_scripts/install-sysroot.py --arch=arm64) that downloads a pre-packaged ARM64 sysroot archive from Google Cloud Storage into the local source tree.

When compiling, the build system passes the --sysroot=<path_to_arm64_sysroot> flag to Clang and LLD. This forces the compiler and linker to ignore the x86_64 host environment and strictly use the headers and libraries inside the ARM64 sysroot.

3. Configuring the Build with GN

Chromium uses GN (Generate Ninja) to generate its build files. To initiate a cross-compile, the developer specifies the target architecture in the args.gn configuration file located in the output directory:

target_cpu = "arm64"

When GN runs, it detects the host architecture automatically (host_cpu = "x64") and reads the desired target (target_cpu = "arm64"). GN then generates the .ninja build manifests, setting up the correct compiler flags, include paths, and sysroot directories for an ARM64 build.

4. Dual Toolchains: Host Tools vs. Target Binaries

The most complex part of cross-compiling Chromium is that the build process requires compiling helper executables and running them immediately to generate code for the rest of the build. Because an x86_64 Linux machine cannot natively execute ARM64 binaries (without QEMU setup), these helper tools must be compiled for x86_64, while the browser itself is compiled for ARM64.

GN handles this by maintaining two active toolchains in the same build graph: the host toolchain (//build/toolchain/linux:clang_x64) and the target toolchain (//build/toolchain/linux:clang_arm64).

  • Code Generators: Tools like protoc (the Protocol Buffers compiler) and Mojo interface generators are assigned to the host toolchain. Ninja compiles these tools into x86_64 binaries using the host's x86_64 sysroot. Ninja then runs these binaries on the host CPU to output C++ source and header files. Finally, those generated C++ files are compiled into ARM64 object files using the target toolchain.
  • The V8 Snapshot Generator: Chromium’s V8 JavaScript engine uses a build-time tool called mksnapshot to initialize the JavaScript heap and dump it into a binary snapshot file, which drastically reduces browser startup time. Because the memory layout of the snapshot is architecture-specific, mksnapshot must generate an ARM64 memory layout. To do this on an x86_64 machine, GN compiles mksnapshot as an x86_64 binary, but configures V8 to use its built-in ARM64 simulator. When Ninja runs mksnapshot on the host, the x86_64 binary simulates an ARM64 CPU in software, executes the initialization code, and outputs a valid ARM64 binary snapshot to be linked into the final browser.

5. Linking with LLD

In the final phase of the build, the Ninja build system invokes the LLVM linker (ld.lld). Like Clang, the x86_64 LLD binary is natively capable of cross-linking. It takes the compiled ARM64 object files (.o), links them against the ARM64 shared objects (.so) located in the downloaded sysroot, embeds the ARM64 V8 snapshot, and outputs the final chrome executable as an ELF 64-bit AArch64 binary.