Nix Tricks

January 30, 2024

In which I compile snippets of useful Nix code

Cross-Compiling Arm Assembly in a Nix Flake

We want to compile this trivial assembly targeting ARM assembly on a x86_64 Linux machine (running Debian 12 with Nix in this case):

  .global main
  .type main, %function
  mov w0, #123

The assembly file is in a file called src/main.s.

The program return 123 as a status code, so we can check whether the program ran correctly later by checking echo $? in bash or similar. We can run the program using qemu-aarch64 (not qemu-system-aarch64) to emulate an ARM CPU and corresponding Linux user land, instead of having to spin up a virtual machine – quite handy when reverse engineering binaries on an x86_64 host.

We want to work with a Nix flake here. That means better support for different targets, and the ability to have better reproducibility between builds, as all Nix dependencies are fixed in a flake.lock file.

I also want to use flake-utils for an easier time specifying the flake file.

  description = "Arm64 (aarch64) cross-compile demo";

  inputs.flake-utils.url = "github:numtide/flake-utils";

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
        pkgs = import nixpkgs {
          inherit system;
          crossSystem = {
            config = "aarch64-unknown-linux-gnu";
        packages = rec {
          cross-arm-64-asm = pkgs.callPackage
            ({ stdenv, gcc }:
              stdenv.mkDerivation {
                name = "cross-arm-64-asm";
                nativeBuildInputs = [
                phases = [ "buildPhase" "installPhase" ];
                buildPhase = ''
                  $CC -march=armv8-a ${./src/main.s} -o main
                installPhase = ''
                  mkdir -p $out/bin
                  cp main $out/bin

            { };
          default = cross-arm-64-asm;

I would be thrilled to hear from you! Please share your thoughts and ideas with me via email.

Back to Index