@@ -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,11 +79,38 @@ with builtins; with lib;
6579 isContainer = true ;
6680
6781 binfmt . registrations = mkIf cfg . interop . register {
68- WSLInterop = {
69- magicOrExtension = "MZ" ;
70- interpreter = "/init" ;
71- fixBinary = true ;
72- } ;
82+ WSLInterop =
83+ let
84+ compat = cfg . compatibility . interopPreserveArgvZero ;
85+
86+ # WSL Preview 0.58 and up registers the /init binfmt interp for Windows executable
87+ # with the "preserve argv[0]" flag, so if you run `./foo.exe`, the interp gets invoked
88+ # as `/init foo.exe ./foo.exe`.
89+ # argv[0] --^ ^-- actual path
90+ #
91+ # Older versions expect to be called without the argv[0] bit, simply as `/init ./foo.exe`.
92+ #
93+ # We detect that by running `/init /known-not-existing-path.exe` and checking the exit code:
94+ # the new style interp expects at least two arguments, so exits with exit code 1,
95+ # presumably meaning "parsing error"; the old style interp attempts to actually run
96+ # the executable, fails to find it, and exits with 255.
97+ compatWrapper = pkgs . writeShellScript "nixos-wsl-binfmt-hack" ''
98+ /init /nixos-wsl-does-not-exist.exe
99+ [ $? -eq 255 ] && shift
100+ exec /init $@
101+ '' ;
102+
103+ # use the autodetect hack if unset, otherwise call /init directly
104+ interpreter = if compat == null then compatWrapper else "/init" ;
105+
106+ # enable for the wrapper and autodetect hack
107+ preserveArgvZero = if compat == false then false else true ;
108+ in
109+ {
110+ magicOrExtension = "MZ" ;
111+ fixBinary = true ;
112+ inherit interpreter preserveArgvZero ;
113+ } ;
73114 } ;
74115 } ;
75116 environment . noXlibs = lib . mkForce false ; # override xlibs not being installed (due to isContainer) to enable the use of GUI apps
0 commit comments