nixos/binfmt: Add option to use static emulators when available (#334859)
This commit is contained in:
+5
-20
@@ -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
|
||||
|
||||
@@ -96,6 +96,7 @@ lib.runTests (
|
||||
canExecute = null;
|
||||
emulator = null;
|
||||
emulatorAvailable = null;
|
||||
staticEmulatorAvailable = null;
|
||||
isCompatible = null;
|
||||
}?${platformAttrName};
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user