Loading lib/systems/default.nix +5 −20 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading lib/tests/systems.nix +1 −0 Original line number Diff line number Diff line Loading @@ -96,6 +96,7 @@ lib.runTests ( canExecute = null; emulator = null; emulatorAvailable = null; staticEmulatorAvailable = null; isCompatible = null; }?${platformAttrName}; }; Loading nixos/modules/system/boot/binfmt.nix +28 −8 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); Loading nixos/tests/systemd-binfmt.nix +25 −0 Original line number Diff line number Diff line Loading @@ -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") ''; }; } pkgs/applications/virtualization/qemu/default.nix +5 −8 Original line number Diff line number Diff line Loading @@ -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 ] Loading @@ -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 ] Loading @@ -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; Loading Loading
lib/systems/default.nix +5 −20 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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 Loading
lib/tests/systems.nix +1 −0 Original line number Diff line number Diff line Loading @@ -96,6 +96,7 @@ lib.runTests ( canExecute = null; emulator = null; emulatorAvailable = null; staticEmulatorAvailable = null; isCompatible = null; }?${platformAttrName}; }; Loading
nixos/modules/system/boot/binfmt.nix +28 −8 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); Loading
nixos/tests/systemd-binfmt.nix +25 −0 Original line number Diff line number Diff line Loading @@ -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") ''; }; }
pkgs/applications/virtualization/qemu/default.nix +5 −8 Original line number Diff line number Diff line Loading @@ -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 ] Loading @@ -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 ] Loading @@ -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; Loading