Setting up nixpkgs for older Linux kernel

I’m interested in using nix on a non-nixos system. The Linux kernel is on the older side though, circa 4.4 I think. What would it be like to set up nixpkgs to build for that?

I found this Nix Pill on nixpkgs having “parameters,” but the only attributes documented there are allowUnfree and pulseaudio.

There’s also literature on overriding packages. I’ve had a quick look at this file nixpkgs/default.nix at 0ae931fb87a4e8241c9c5233a4eacccfa00fd68b · NixOS/nixpkgs · GitHub, but I’d need to learn a lot more about the expression language before I can figure out how to make an override for it.

The actual way this came up, if anyone’s curious, is that glibc-2.33 (used in nixpkgs since Jul 2021) uses faccessat2 glibc/faccessat.c at 9826b03b747b841f5fc6de2054bf1ef3f5c4bdf3 · bminor/glibc · GitHub, and as nixpkgs configures it with kernel headers from 5.8+, glibc/kernel-features.h at 9826b03b747b841f5fc6de2054bf1ef3f5c4bdf3 · bminor/glibc · GitHub does not fall back to its emulation. That system call isn’t available before 5.8, and it makes a lot of checks appear as if they don’t have access to the file they asked about. This results in stdenv’s [[ -x ./configure ]] falsely concluding that there’s no configure script, which results in no Makefile being produced, which results in nothing actually being built for most packages.

Another possibly inconvenient thing is that, as I understand Nix, even if I did correctly configure the kernel headers, its output would rightly be different, glibc would be treated as different, and I would have to recompile, oh, only anything that depends on glibc. For myself, I was going to do that anyway because I was interested in using a different store path, so whatever. But I’m curious if there’s any way to avoid that if I would use /nix/store.

Folks, do you have any ideas or advice for this situation?

In my experience hanging on to older kernels is often more trouble than it’s worth, except if you’re talking about a (semi-) embedded system (like e.g. a NAS).

That said, maybe a quick and dirty way is to just pull in old nixpkgs, you can check which version of the nixpkgs tree contains which package version here.
This has the huge disadvantage that you get old packages for everything though, so I’d wouldn’t consider this a real solution for most use cases. (I tried to use this for compatibility testing of binary packages for old systems, but due to pure/impure issues found containers to work better for that).

I guess the only way to do it cleanly is using overrides like you already mentioned, and indeed that requires a bit more understanding of the matter to use effectively. Maybe someone more knowledgeable than myself has a snippet ready for that too, otherwise you may have to dig in a bit…

1 Like

an update on this:

  • I’m going down the overlay route
  • seemed like it would be easy enough since nixpkgs gives us a makeLinuxHeaders function and we can pass it whatever version we desire
  • but the header build steps have changed in linux 5.x, so we makeLinuxHeaders doesn’t work
  • I’m instead doing this hacky thing of assembling the hosts’s own installed kernel headers (ubuntu; linux-libc-dev package) into a nix package
  • still some things seem to be broken, and the resulting coreutils-9.0 fails some tests (:pray: for the maintainers who haven’t disabled the tests when building) where rm always says permissions denied
    • probably gonna be tricky to debug, I’m on an unprivileged account with no permission to run strace
    • I reckon I’ll nix-build -K and add some printf’s

I missed this part of nixpkgs’s glibc, it’s actually been compiled with --enable-kernel=3.2.0 all along. Thus it had always used the fallback for when faccessat2 was not available.

I ended up writing a little test program to call syscall(439, ...) directly to get a closer look at what was happening, and it wasn’t even giving ENOSYS as expected. This is probably due to some unrelated issue with the container software on this system coming from the era when people used seccomp to return EPERM on unknown syscalls.

As it happened a couple of years ago, glibc indeed started using faccessat2, a bunch of stuff broke, and container systems switched to ENOSYS. This particular container hosting service unfortunately never updated.

Clearly the way to proceed is to upgrade the container software so that things go back to working. But if I could realistically ask the system administrator to upgrade the container software, I would sooner ask them to update a few packages and not have to set up a whole Nix installation. Thus, onward to learning how to add custom patches to glibc!

This should be the final update:

I didn’t even need to write my own patch, because I found one online. I added this file, ~/.config/nixpkgs/overlays/glibc.nix with

self: super:

{
  glibc = super.glibc.overrideAttrs (old: {
    patches = (old.patches or []) ++ [
      (self.fetchurl {
        url = "https://src.fedoraproject.org/rpms/glibc/raw/98f35706db9bcb0bd505b3228514cf8ecebe9af2/f/glibc-rhbz1869030-faccessat2-eperm.patch";
        sha256 = "0hcqi4j3f897hf1xzqpah6d2xdz7kn7dbbmsbrrk79iliq8qxrgd";
      })
    ];
  });
}

and the glibc tests passed, and the coreutils tests passed, and later down the line packages were even able to be configured, made, and installed

2 Likes

Probably not significant, but fetchpatch instead of fetchurl helps ensure patches actually work :slight_smile:

we’re fortunate that this patch succeeds as-is :sweat_smile:

a comment in the glibc common.nix advises:

Note: this package is used for bootstrapping fetchurl, and thus
cannot use fetchpatch! All mutable patches (generated by GitHub or
cgit) that are needed here should be included directly in Nixpkgs as
files.

1 Like