NixOS and Actually Portable Executables

Posted on January 15, 2025 by Jack Kelly
Tags: nix, cosmopolitan, coding

I’ve been very impressed by the Cosmopolitan Libc project and the αcτµαlly pδrταblε εxεcµταblε file format that makes it all possible. Building a crazy polyglot binary that works on two architectures and half-a-dozen operating systems is one thing, but Mozilla’s Llamafile project showed that APEs were more than just a cool hack: it’s stunning that I just download a reasonably smart LLM to my computer, chmod +x it, and begin a conversation. LLM use-cases aside, single-file dependency-free programs that work basically anywhere are just a great thing to be able to stick on a USB stick.

But APEs have been a little finicky for me. I run NixOS as my primary operating system, and sometimes an APE will refuse to launch, or I’ll need to pass --assimilate to it and permanently turn it into an x86-64 Linux binary before it’ll work.

The Cosmopolitan project, of course, has a couple of answers ready. One is to install a system-wide APE loader separate from any one binary and register it with binfmt_misc; the other is to patch the kernel so it can natively detect APEs. I don’t want to wait for a full kernel compile each time I need to update, so until the patch lands in mainline binfmt_misc is the way to go.

I have set up a Nix Flake at https://git.sr.ht/~jack/cosmo.nix to hold my experiments joining the Nix and Cosmopolitan universes. For now, it has convenient NixOS options to register the APE loader with binfmt_misc:`

{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";

    # Import cosmo.nix and plumb our nixpkgs rev through it
    cosmo-nix = {
      url = "sourcehut:~jack/cosmo.nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = inputs@{ nixpkgs }: {
    nixosConfigurations.myMachine = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        # Include the cosmo.nix module...
        inputs.cosmo-nix.nixosModules.default

        # ...and register the ape-loader with binfmt_misc
        { programs.ape-loader.binfmt = true; }

        # Your other NixOS config here.
      ];
    }
  }
}

Not bad for an evening’s hacking! I hope to take it further at some point in the future: I’d like to build the loader itself from a known bootstrap path rather than fetching a binary, and I’d especially love to be able to do the same with Cosmopolitan Libc and cosmocc. I’m a big fan of bootstrappable builds, and reliable building of ultraportable binaries from a documented bootstrap path would be an incredibly cool achievement. I currently get so much useful stuff out of nix develop — all my build dependencies, pre-commit hooks, useful CI targets, and more — and I have projects in mind where I want Cosmopolitan Libc to be a “tier 1” platform. Nix-based access to a cosmocc toolchain and all cosmocc-built build dependencies would make that so much easier.

Previous Post
All Posts | RSS | Atom
Copyright © 2025 Jack Kelly
Site generated by Hakyll (source)