@@ -42,6 +42,20 @@ with builtins; with lib;
42
42
description = "Include Windows PATH in WSL PATH" ;
43
43
} ;
44
44
} ;
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
+ } ;
45
59
} ;
46
60
47
61
config =
@@ -65,11 +79,38 @@ with builtins; with lib;
65
79
isContainer = true ;
66
80
67
81
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
+ } ;
73
114
} ;
74
115
} ;
75
116
environment . noXlibs = lib . mkForce false ; # override xlibs not being installed (due to isContainer) to enable the use of GUI apps
0 commit comments