Skip to content

Commit a3a1bcc

Browse files
committed
interop: enable preserveArgvZero
Fixes interop with latest WSL versions
1 parent 80c18db commit a3a1bcc

File tree

1 file changed

+41
-2
lines changed

1 file changed

+41
-2
lines changed

modules/wsl-distro.nix

+41-2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ with builtins; with lib;
4242
description = "Include Windows PATH in WSL PATH";
4343
};
4444
};
45+
46+
compatibility = {
47+
interopPreserveArgvZero = mkOption {
48+
type = nullOr bool;
49+
default = null;
50+
description = ''
51+
Register binfmt interpreter for Windows executables with 'preserves argv[0]' flag.
52+
53+
Default (null): autodetect, at some performance cost.
54+
To avoid the performance cost, set this to true for WSL Preview 0.58 and up,
55+
or to false for older versions (including pre-Microsoft Store).
56+
'';
57+
};
58+
};
4559
};
4660

4761
config =
@@ -65,10 +79,35 @@ with builtins; with lib;
6579
isContainer = true;
6680

6781
binfmt.registrations = mkIf cfg.interop.register {
68-
WSLInterop = {
82+
WSLInterop = let
83+
compat = cfg.compatibility.interopPreserveArgvZero;
84+
85+
# WSL Preview 0.58 and up registers the /init binfmt interp for Windows executable
86+
# with the "preserve argv[0]" flag, so if you run `./foo.exe`, the interp gets invoked
87+
# as `/init foo.exe ./foo.exe`.
88+
# argv[0] --^ ^-- actual path
89+
#
90+
# Older versions expect to be called without the argv[0] bit, simply as `/init ./foo.exe`.
91+
#
92+
# We detect that by running `/init /known-not-existing-path.exe` and checking the exit code:
93+
# the new style interp expects at least two arguments, so exits with exit code 1,
94+
# presumably meaning "parsing error"; the old style interp attempts to actually run
95+
# the executable, fails to find it, and exits with 255.
96+
compatWrapper = pkgs.writeShellScript "nixos-wsl-binfmt-hack" ''
97+
/init /nixos-wsl-does-not-exist.exe
98+
[ $? -eq 255 ] && shift
99+
exec /init $@
100+
'';
101+
102+
# use the autodetect hack if unset, otherwise call /init directly
103+
interpreter = if compat == null then compatWrapper else "/init";
104+
105+
# enable for the wrapper and autodetect hack
106+
preserveArgvZero = if compat == false then false else true;
107+
in {
69108
magicOrExtension = "MZ";
70-
interpreter = "/init";
71109
fixBinary = true;
110+
inherit interpreter preserveArgvZero;
72111
};
73112
};
74113
};

0 commit comments

Comments
 (0)