nixos/binfmt: Preserve argv[0] when using QEMU
This commit is contained in:
parent
3011531cf9
commit
9e5d0a9458
|
@ -20,16 +20,20 @@ let
|
||||||
optionalString fixBinary "F";
|
optionalString fixBinary "F";
|
||||||
in ":${name}:${type}:${offset'}:${magicOrExtension}:${mask'}:${interpreter}:${flags}";
|
in ":${name}:${type}:${offset'}:${magicOrExtension}:${mask'}:${interpreter}:${flags}";
|
||||||
|
|
||||||
activationSnippet = name: { interpreter, ... }: ''
|
activationSnippet = name: { interpreter, wrapInterpreterInShell, ... }: if wrapInterpreterInShell then ''
|
||||||
rm -f /run/binfmt/${name}
|
rm -f /run/binfmt/${name}
|
||||||
cat > /run/binfmt/${name} << 'EOF'
|
cat > /run/binfmt/${name} << 'EOF'
|
||||||
#!${pkgs.bash}/bin/sh
|
#!${pkgs.bash}/bin/sh
|
||||||
exec -- ${interpreter} "$@"
|
exec -- ${interpreter} "$@"
|
||||||
EOF
|
EOF
|
||||||
chmod +x /run/binfmt/${name}
|
chmod +x /run/binfmt/${name}
|
||||||
|
'' else ''
|
||||||
|
rm -f /run/binfmt/${name}
|
||||||
|
ln -s ${interpreter} /run/binfmt/${name}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
getEmulator = system: (lib.systems.elaborate { inherit system; }).emulator pkgs;
|
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:
|
# Mapping of systems to “magicOrExtension” and “mask”. Mostly taken from:
|
||||||
# - https://github.com/cleverca22/nixos-configs/blob/master/qemu.nix
|
# - https://github.com/cleverca22/nixos-configs/blob/master/qemu.nix
|
||||||
|
@ -238,6 +242,25 @@ in {
|
||||||
'';
|
'';
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
wrapInterpreterInShell = mkOption {
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether to wrap the interpreter in a shell script.
|
||||||
|
|
||||||
|
This allows a shell command to be set as the interpreter.
|
||||||
|
'';
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
|
||||||
|
interpreterSandboxPath = mkOption {
|
||||||
|
internal = true;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Path of the interpreter to expose in the build sandbox.
|
||||||
|
'';
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
@ -257,16 +280,37 @@ in {
|
||||||
config = {
|
config = {
|
||||||
boot.binfmt.registrations = builtins.listToAttrs (map (system: {
|
boot.binfmt.registrations = builtins.listToAttrs (map (system: {
|
||||||
name = system;
|
name = system;
|
||||||
value = {
|
value = let
|
||||||
interpreter = getEmulator system;
|
interpreter = getEmulator system;
|
||||||
|
qemuArch = getQemuArch system;
|
||||||
|
|
||||||
|
preserveArgvZero = "qemu-${qemuArch}" == baseNameOf interpreter;
|
||||||
|
interpreterReg = let
|
||||||
|
wrapperName = "qemu-${qemuArch}-binfmt-P";
|
||||||
|
wrapper = pkgs.wrapQemuBinfmtP wrapperName interpreter;
|
||||||
|
in
|
||||||
|
if preserveArgvZero then "${wrapper}/bin/${wrapperName}"
|
||||||
|
else interpreter;
|
||||||
|
in {
|
||||||
|
inherit preserveArgvZero;
|
||||||
|
|
||||||
|
interpreter = interpreterReg;
|
||||||
|
wrapInterpreterInShell = !preserveArgvZero;
|
||||||
|
interpreterSandboxPath = dirOf (dirOf interpreterReg);
|
||||||
} // (magics.${system} or (throw "Cannot create binfmt registration for system ${system}"));
|
} // (magics.${system} or (throw "Cannot create binfmt registration for system ${system}"));
|
||||||
}) cfg.emulatedSystems);
|
}) cfg.emulatedSystems);
|
||||||
# TODO: add a nix.extraPlatforms option to NixOS!
|
# TODO: add a nix.extraPlatforms option to NixOS!
|
||||||
nix.extraOptions = lib.mkIf (cfg.emulatedSystems != []) ''
|
nix.extraOptions = lib.mkIf (cfg.emulatedSystems != []) ''
|
||||||
extra-platforms = ${toString (cfg.emulatedSystems ++ lib.optional pkgs.stdenv.hostPlatform.isx86_64 "i686-linux")}
|
extra-platforms = ${toString (cfg.emulatedSystems ++ lib.optional pkgs.stdenv.hostPlatform.isx86_64 "i686-linux")}
|
||||||
'';
|
'';
|
||||||
nix.sandboxPaths = lib.mkIf (cfg.emulatedSystems != [])
|
nix.sandboxPaths = lib.mkIf (cfg.emulatedSystems != []) (
|
||||||
([ "/run/binfmt" "${pkgs.bash}" ] ++ (map (system: dirOf (dirOf (getEmulator system))) cfg.emulatedSystems));
|
let
|
||||||
|
ruleFor = system: cfg.registrations.${system};
|
||||||
|
hasWrappedRule = lib.any (system: (ruleFor system).wrapInterpreterInShell) cfg.emulatedSystems;
|
||||||
|
in [ "/run/binfmt" ]
|
||||||
|
++ lib.optional hasWrappedRule "${pkgs.bash}"
|
||||||
|
++ (map (system: (ruleFor system).interpreterSandboxPath) cfg.emulatedSystems)
|
||||||
|
);
|
||||||
|
|
||||||
environment.etc."binfmt.d/nixos.conf".source = builtins.toFile "binfmt_nixos.conf"
|
environment.etc."binfmt.d/nixos.conf".source = builtins.toFile "binfmt_nixos.conf"
|
||||||
(lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine config.boot.binfmt.registrations));
|
(lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine config.boot.binfmt.registrations));
|
||||||
|
|
|
@ -1,24 +1,71 @@
|
||||||
# Teach the kernel how to run armv7l and aarch64-linux binaries,
|
# Teach the kernel how to run armv7l and aarch64-linux binaries,
|
||||||
# and run GNU Hello for these architectures.
|
# and run GNU Hello for these architectures.
|
||||||
import ./make-test-python.nix ({ pkgs, ... }: {
|
|
||||||
name = "systemd-binfmt";
|
{ system ? builtins.currentSystem,
|
||||||
machine = {
|
config ? {},
|
||||||
boot.binfmt.emulatedSystems = [
|
pkgs ? import ../.. { inherit system config; }
|
||||||
"armv7l-linux"
|
}:
|
||||||
"aarch64-linux"
|
|
||||||
];
|
with import ../lib/testing-python.nix { inherit system pkgs; };
|
||||||
|
|
||||||
|
let
|
||||||
|
expectArgv0 = xpkgs: xpkgs.runCommandCC "expect-argv0" {
|
||||||
|
src = pkgs.writeText "expect-argv0.c" ''
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
fprintf(stderr, "Our argv[0] is %s\n", argv[0]);
|
||||||
|
|
||||||
|
if (strcmp(argv[0], argv[1])) {
|
||||||
|
fprintf(stderr, "ERROR: argv[0] is %s, should be %s\n", argv[0], argv[1]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
} ''
|
||||||
|
$CC -o $out $src
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
basic = makeTest {
|
||||||
|
name = "systemd-binfmt";
|
||||||
|
machine = {
|
||||||
|
boot.binfmt.emulatedSystems = [
|
||||||
|
"armv7l-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = let
|
||||||
|
helloArmv7l = pkgs.pkgsCross.armv7l-hf-multiplatform.hello;
|
||||||
|
helloAarch64 = pkgs.pkgsCross.aarch64-multiplatform.hello;
|
||||||
|
in ''
|
||||||
|
machine.start()
|
||||||
|
|
||||||
|
assert "world" in machine.succeed(
|
||||||
|
"${helloArmv7l}/bin/hello"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "world" in machine.succeed(
|
||||||
|
"${helloAarch64}/bin/hello"
|
||||||
|
)
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
testScript = let
|
preserveArgvZero = makeTest {
|
||||||
helloArmv7l = pkgs.pkgsCross.armv7l-hf-multiplatform.hello;
|
name = "systemd-binfmt-preserve-argv0";
|
||||||
helloAarch64 = pkgs.pkgsCross.aarch64-multiplatform.hello;
|
machine = {
|
||||||
in ''
|
boot.binfmt.emulatedSystems = [
|
||||||
machine.start()
|
"aarch64-linux"
|
||||||
assert "world" in machine.succeed(
|
];
|
||||||
"${helloArmv7l}/bin/hello"
|
};
|
||||||
)
|
testScript = let
|
||||||
assert "world" in machine.succeed(
|
testAarch64 = expectArgv0 pkgs.pkgsCross.aarch64-multiplatform;
|
||||||
"${helloAarch64}/bin/hello"
|
in ''
|
||||||
)
|
machine.start()
|
||||||
'';
|
machine.succeed("exec -a meow ${testAarch64} meow")
|
||||||
})
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue