@@ -20,16 +20,20 @@ let
2020 optionalString fixBinary "F";
2121 in ":${name}:${type}:${offset'}:${magicOrExtension}:${mask'}:${interpreter}:${flags}";
2222
23− activationSnippet = name: { interpreter, ... }: ''
23+ activationSnippet = name: { interpreter, wrapInterpreterInShell, ... }: if wrapInterpreterInShell then ''
2424 rm -f /run/binfmt/${name}
2525 cat > /run/binfmt/${name} << 'EOF'
2626 #!${pkgs.bash}/bin/sh
2727 exec -- ${interpreter} "$@"
2828 EOF
2929 chmod +x /run/binfmt/${name}
30+ '' else ''
31+ rm -f /run/binfmt/${name}
32+ ln -s ${interpreter} /run/binfmt/${name}
3033 '';
3134
3235 getEmulator = system: (lib.systems.elaborate { inherit system; }).emulator pkgs;
36+ getQemuArch = system: (lib.systems.elaborate { inherit system; }).qemuArch;
3337
3438 # Mapping of systems to “magicOrExtension” and “mask”. Mostly taken from:
3539 # - https://github.com/cleverca22/nixos-configs/blob/master/qemu.nix
@@ -238,6 +242,25 @@ in {
238242 '';
239243 type = types.bool;
240244 };
245+
246+ wrapInterpreterInShell = mkOption {
247+ default = true;
248+ description = ''
249+ Whether to wrap the interpreter in a shell script.
250+
251+ This allows a shell command to be set as the interpreter.
252+ '';
253+ type = types.bool;
254+ };
255+
256+ interpreterSandboxPath = mkOption {
257+ internal = true;
258+ default = null;
259+ description = ''
260+ Path of the interpreter to expose in the build sandbox.
261+ '';
262+ type = types.nullOr types.path;
263+ };
241264 };
242265 }));
243266 };
@@ -258,16 +281,37 @@ in {
258281 config = {
259282 boot.binfmt.registrations = builtins.listToAttrs (map (system: {
260283 name = system;
261− value = {
284+ value = let
262285 interpreter = getEmulator system;
286+ qemuArch = getQemuArch system;
287+
288+ preserveArgvZero = "qemu-${qemuArch}" == baseNameOf interpreter;
289+ interpreterReg = let
290+ wrapperName = "qemu-${qemuArch}-binfmt-P";
291+ wrapper = pkgs.wrapQemuBinfmtP wrapperName interpreter;
292+ in
293+ if preserveArgvZero then "${wrapper}/bin/${wrapperName}"
294+ else interpreter;
295+ in {
296+ inherit preserveArgvZero;
297+
298+ interpreter = interpreterReg;
299+ wrapInterpreterInShell = !preserveArgvZero;
300+ interpreterSandboxPath = dirOf (dirOf interpreterReg);
263301 } // (magics.${system} or (throw "Cannot create binfmt registration for system ${system}"));
264302 }) cfg.emulatedSystems);
265303 # TODO: add a nix.extraPlatforms option to NixOS!
266304 nix.extraOptions = lib.mkIf (cfg.emulatedSystems != []) ''
267305 extra-platforms = ${toString (cfg.emulatedSystems ++ lib.optional pkgs.stdenv.hostPlatform.isx86_64 "i686-linux")}
268306 '';
269− nix.sandboxPaths = lib.mkIf (cfg.emulatedSystems != [])
270− ([ "/run/binfmt" "${pkgs.bash}" ] ++ (map (system: dirOf (dirOf (getEmulator system))) cfg.emulatedSystems));
307+ nix.sandboxPaths = lib.mkIf (cfg.emulatedSystems != []) (
308+ let
309+ ruleFor = system: cfg.registrations.${system};
310+ hasWrappedRule = lib.any (system: (ruleFor system).wrapInterpreterInShell) cfg.emulatedSystems;
311+ in [ "/run/binfmt" ]
312+ ++ lib.optional hasWrappedRule "${pkgs.bash}"
313+ ++ (map (system: (ruleFor system).interpreterSandboxPath) cfg.emulatedSystems)
314+ );
271315
272316 environment.etc."binfmt.d/nixos.conf".source = builtins.toFile "binfmt_nixos.conf"
273317 (lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine config.boot.binfmt.registrations));