nixos/binfmt: Add option to use static emulators when available (#334859)

This commit is contained in:
Sandro
2024-10-02 15:51:30 +02:00
committed by GitHub
5 changed files with 64 additions and 36 deletions
+5 -20
View File
@@ -277,25 +277,6 @@ let
let
selectEmulator = pkgs:
let
qemu-user = pkgs.qemu.override {
smartcardSupport = false;
spiceSupport = false;
openGLSupport = false;
virglSupport = false;
vncSupport = false;
gtkSupport = false;
sdlSupport = false;
alsaSupport = false;
pulseSupport = false;
pipewireSupport = false;
jackSupport = false;
smbdSupport = false;
seccompSupport = false;
tpmSupport = false;
capstoneSupport = false;
enableDocs = false;
hostCpuTargets = [ "${final.qemuArch}-linux-user" ];
};
wine = (pkgs.winePackagesFor "wine${toString final.parsed.cpu.bits}").minimal;
in
# Note: we guarantee that the return value is either `null` or a path
@@ -306,7 +287,7 @@ let
else if final.isWindows
then "${wine}/bin/wine${optionalString (final.parsed.cpu.bits == 64) "64"}"
else if final.isLinux && pkgs.stdenv.hostPlatform.isLinux && final.qemuArch != null
then "${qemu-user}/bin/qemu-${final.qemuArch}"
then "${pkgs.qemu-user}/bin/qemu-${final.qemuArch}"
else if final.isWasi
then "${pkgs.wasmtime}/bin/wasmtime"
else if final.isMmix
@@ -315,6 +296,10 @@ let
in {
emulatorAvailable = pkgs: (selectEmulator pkgs) != null;
# whether final.emulator pkgs.pkgsStatic works
staticEmulatorAvailable = pkgs: final.emulatorAvailable pkgs
&& (final.isLinux || final.isWasi || final.isMmix);
emulator = pkgs:
if (final.emulatorAvailable pkgs)
then selectEmulator pkgs
+1
View File
@@ -96,6 +96,7 @@ lib.runTests (
canExecute = null;
emulator = null;
emulatorAvailable = null;
staticEmulatorAvailable = null;
isCompatible = null;
}?${platformAttrName};
};
+28 -8
View File
@@ -28,8 +28,6 @@ let
''
else interpreter;
getEmulator = system: (lib.systems.elaborate { inherit system; }).emulator pkgs;
getQemuArch = system: (lib.systems.elaborate { inherit system; }).qemuArch;
# Mapping of systems to “magicOrExtension” and “mask”. Mostly taken from:
# - https://github.com/cleverca22/nixos-configs/blob/master/qemu.nix
@@ -280,28 +278,50 @@ in {
'';
type = types.listOf (types.enum (builtins.attrNames magics));
};
preferStaticEmulators = mkOption {
default = false;
description = ''
Whether to use static emulators when available.
This enables the kernel to preload the emulator binaries when
the binfmt registrations are added, obviating the need to make
the emulator binaries available inside chroots and chroot-like
sandboxes.
'';
type = types.bool;
};
};
};
config = {
assertions = lib.mapAttrsToList (name: reg: {
assertion = reg.fixBinary -> !reg.wrapInterpreterInShell;
message = "boot.binfmt.registrations.\"${name}\" cannot have fixBinary when the interpreter is invoked through a shell.";
}) cfg.registrations;
boot.binfmt.registrations = builtins.listToAttrs (map (system: assert system != pkgs.stdenv.hostPlatform.system; {
name = system;
value = { config, ... }: let
interpreter = getEmulator system;
qemuArch = getQemuArch system;
elaborated = lib.systems.elaborate { inherit system; };
useStaticEmulator = cfg.preferStaticEmulators && elaborated.staticEmulatorAvailable pkgs;
interpreter = elaborated.emulator (if useStaticEmulator then pkgs.pkgsStatic else pkgs);
inherit (elaborated) qemuArch;
isQemu = "qemu-${qemuArch}" == baseNameOf interpreter;
preserveArgvZero = "qemu-${qemuArch}" == baseNameOf interpreter;
interpreterReg = let
wrapperName = "qemu-${qemuArch}-binfmt-P";
wrapper = pkgs.wrapQemuBinfmtP wrapperName interpreter;
in
if preserveArgvZero then "${wrapper}/bin/${wrapperName}"
if isQemu && !useStaticEmulator then "${wrapper}/bin/${wrapperName}"
else interpreter;
in ({
preserveArgvZero = mkDefault preserveArgvZero;
preserveArgvZero = mkDefault isQemu;
interpreter = mkDefault interpreterReg;
wrapInterpreterInShell = mkDefault (!config.preserveArgvZero);
fixBinary = mkDefault useStaticEmulator;
wrapInterpreterInShell = mkDefault (!config.preserveArgvZero && !config.fixBinary);
interpreterSandboxPath = mkDefault (dirOf (dirOf config.interpreter));
} // (magics.${system} or (throw "Cannot create binfmt registration for system ${system}")));
}) cfg.emulatedSystems);
+25
View File
@@ -87,4 +87,29 @@ in {
).lower()
'';
};
chroot = makeTest {
name = "systemd-binfmt-chroot";
nodes.machine = { pkgs, lib, ... }: {
boot.binfmt.emulatedSystems = [
"aarch64-linux" "wasm32-wasi"
];
boot.binfmt.preferStaticEmulators = true;
environment.systemPackages = [
(pkgs.writeShellScriptBin "test-chroot" ''
set -euo pipefail
mkdir -p /tmp/chroot
cp ${lib.getExe' pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic.busybox "busybox"} /tmp/chroot/busybox
cp ${lib.getExe pkgs.pkgsCross.wasi32.yaml2json} /tmp/chroot/yaml2json # wasi binaries that build are hard to come by
chroot /tmp/chroot /busybox uname -m | grep aarch64
echo 42 | chroot /tmp/chroot /yaml2json | grep 42
'')
];
};
testScript = ''
machine.start()
machine.succeed("test-chroot")
'';
};
}
@@ -94,10 +94,9 @@ stdenv.mkDerivation (finalAttrs: {
++ lib.optionals stdenv.hostPlatform.isDarwin [ sigtool ]
++ lib.optionals (!userOnly) [ dtc ];
buildInputs = [ zlib glib pixman
vde2 lzo snappy libtasn1
gnutls nettle curl libslirp
]
buildInputs = [ glib zlib ]
++ lib.optionals (!minimal) [ dtc pixman vde2 lzo snappy libtasn1 gnutls nettle libslirp ]
++ lib.optionals (!userOnly) [ curl ]
++ lib.optionals ncursesSupport [ ncurses ]
++ lib.optionals stdenv.hostPlatform.isDarwin [ CoreServices Cocoa Hypervisor Kernel rez setfile vmnet ]
++ lib.optionals seccompSupport [ libseccomp ]
@@ -112,8 +111,7 @@ stdenv.mkDerivation (finalAttrs: {
++ lib.optionals smartcardSupport [ libcacard ]
++ lib.optionals spiceSupport [ spice-protocol spice ]
++ lib.optionals usbredirSupport [ usbredir ]
++ lib.optionals stdenv.hostPlatform.isLinux [ libcap_ng libcap attr ]
++ lib.optionals (stdenv.hostPlatform.isLinux && !userOnly) [ libaio ]
++ lib.optionals (stdenv.hostPlatform.isLinux && !userOnly) [ libcap_ng libcap attr libaio ]
++ lib.optionals xenSupport [ xen ]
++ lib.optionals cephSupport [ ceph ]
++ lib.optionals glusterfsSupport [ glusterfs libuuid ]
@@ -124,8 +122,7 @@ stdenv.mkDerivation (finalAttrs: {
++ lib.optionals smbdSupport [ samba ]
++ lib.optionals uringSupport [ liburing ]
++ lib.optionals canokeySupport [ canokey-qemu ]
++ lib.optionals capstoneSupport [ capstone ]
++ lib.optionals (!userOnly) [ dtc ];
++ lib.optionals capstoneSupport [ capstone ];
dontUseMesonConfigure = true; # meson's configurePhase isn't compatible with qemu build
dontAddStaticConfigureFlags = true;