at 23.05-pre 14 kB view raw
1{ config, lib, pkgs, ... }: 2let 3 inherit (lib) mkOption types optionalString stringAfter; 4 5 cfg = config.boot.binfmt; 6 7 makeBinfmtLine = name: { recognitionType, offset, magicOrExtension 8 , mask, preserveArgvZero, openBinary 9 , matchCredentials, fixBinary, ... 10 }: let 11 type = if recognitionType == "magic" then "M" else "E"; 12 offset' = toString offset; 13 mask' = toString mask; 14 interpreter = "/run/binfmt/${name}"; 15 flags = if !(matchCredentials -> openBinary) 16 then throw "boot.binfmt.registrations.${name}: you can't specify openBinary = false when matchCredentials = true." 17 else optionalString preserveArgvZero "P" + 18 optionalString (openBinary && !matchCredentials) "O" + 19 optionalString matchCredentials "C" + 20 optionalString fixBinary "F"; 21 in ":${name}:${type}:${offset'}:${magicOrExtension}:${mask'}:${interpreter}:${flags}"; 22 23 activationSnippet = name: { interpreter, wrapInterpreterInShell, ... }: if wrapInterpreterInShell then '' 24 rm -f /run/binfmt/${name} 25 cat > /run/binfmt/${name} << 'EOF' 26 #!${pkgs.bash}/bin/sh 27 exec -- ${interpreter} "$@" 28 EOF 29 chmod +x /run/binfmt/${name} 30 '' else '' 31 rm -f /run/binfmt/${name} 32 ln -s ${interpreter} /run/binfmt/${name} 33 ''; 34 35 getEmulator = system: (lib.systems.elaborate { inherit system; }).emulator pkgs; 36 getQemuArch = system: (lib.systems.elaborate { inherit system; }).qemuArch; 37 38 # Mapping of systems to “magicOrExtension” and “mask”. Mostly taken from: 39 # - https://github.com/cleverca22/nixos-configs/blob/master/qemu.nix 40 # and 41 # - https://github.com/qemu/qemu/blob/master/scripts/qemu-binfmt-conf.sh 42 # TODO: maybe put these in a JSON file? 43 magics = { 44 armv6l-linux = { 45 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00''; 46 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff''; 47 }; 48 armv7l-linux = { 49 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00''; 50 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff''; 51 }; 52 aarch64-linux = { 53 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00''; 54 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff''; 55 }; 56 aarch64_be-linux = { 57 magicOrExtension = ''\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7''; 58 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 59 }; 60 i386-linux = { 61 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00''; 62 mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 63 }; 64 i486-linux = { 65 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00''; 66 mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 67 }; 68 i586-linux = { 69 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00''; 70 mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 71 }; 72 i686-linux = { 73 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00''; 74 mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 75 }; 76 x86_64-linux = { 77 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00''; 78 mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 79 }; 80 alpha-linux = { 81 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90''; 82 mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 83 }; 84 sparc64-linux = { 85 magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02''; 86 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 87 }; 88 sparc-linux = { 89 magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x12''; 90 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 91 }; 92 powerpc-linux = { 93 magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14''; 94 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 95 }; 96 powerpc64-linux = { 97 magicOrExtension = ''\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15''; 98 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 99 }; 100 powerpc64le-linux = { 101 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15\x00''; 102 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\x00''; 103 }; 104 mips-linux = { 105 magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08''; 106 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 107 }; 108 mipsel-linux = { 109 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00''; 110 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 111 }; 112 mips64-linux = { 113 magicOrExtension = ''\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08''; 114 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 115 }; 116 mips64el-linux = { 117 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00''; 118 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 119 }; 120 riscv32-linux = { 121 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00''; 122 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 123 }; 124 riscv64-linux = { 125 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00''; 126 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 127 }; 128 wasm32-wasi = { 129 magicOrExtension = ''\x00asm''; 130 mask = ''\xff\xff\xff\xff''; 131 }; 132 wasm64-wasi = { 133 magicOrExtension = ''\x00asm''; 134 mask = ''\xff\xff\xff\xff''; 135 }; 136 x86_64-windows = { 137 magicOrExtension = ".exe"; 138 recognitionType = "extension"; 139 }; 140 i686-windows = { 141 magicOrExtension = ".exe"; 142 recognitionType = "extension"; 143 }; 144 }; 145 146in { 147 imports = [ 148 (lib.mkRenamedOptionModule [ "boot" "binfmtMiscRegistrations" ] [ "boot" "binfmt" "registrations" ]) 149 ]; 150 151 options = { 152 boot.binfmt = { 153 registrations = mkOption { 154 default = {}; 155 156 description = lib.mdDoc '' 157 Extra binary formats to register with the kernel. 158 See https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html for more details. 159 ''; 160 161 type = types.attrsOf (types.submodule ({ config, ... }: { 162 options = { 163 recognitionType = mkOption { 164 default = "magic"; 165 description = lib.mdDoc "Whether to recognize executables by magic number or extension."; 166 type = types.enum [ "magic" "extension" ]; 167 }; 168 169 offset = mkOption { 170 default = null; 171 description = lib.mdDoc "The byte offset of the magic number used for recognition."; 172 type = types.nullOr types.int; 173 }; 174 175 magicOrExtension = mkOption { 176 description = lib.mdDoc "The magic number or extension to match on."; 177 type = types.str; 178 }; 179 180 mask = mkOption { 181 default = null; 182 description = 183 lib.mdDoc "A mask to be ANDed with the byte sequence of the file before matching"; 184 type = types.nullOr types.str; 185 }; 186 187 interpreter = mkOption { 188 description = lib.mdDoc '' 189 The interpreter to invoke to run the program. 190 191 Note that the actual registration will point to 192 /run/binfmt/''${name}, so the kernel interpreter length 193 limit doesn't apply. 194 ''; 195 type = types.path; 196 }; 197 198 preserveArgvZero = mkOption { 199 default = false; 200 description = lib.mdDoc '' 201 Whether to pass the original argv[0] to the interpreter. 202 203 See the description of the 'P' flag in the kernel docs 204 for more details; 205 ''; 206 type = types.bool; 207 }; 208 209 openBinary = mkOption { 210 default = config.matchCredentials; 211 description = lib.mdDoc '' 212 Whether to pass the binary to the interpreter as an open 213 file descriptor, instead of a path. 214 ''; 215 type = types.bool; 216 }; 217 218 matchCredentials = mkOption { 219 default = false; 220 description = lib.mdDoc '' 221 Whether to launch with the credentials and security 222 token of the binary, not the interpreter (e.g. setuid 223 bit). 224 225 See the description of the 'C' flag in the kernel docs 226 for more details. 227 228 Implies/requires openBinary = true. 229 ''; 230 type = types.bool; 231 }; 232 233 fixBinary = mkOption { 234 default = false; 235 description = lib.mdDoc '' 236 Whether to open the interpreter file as soon as the 237 registration is loaded, rather than waiting for a 238 relevant file to be invoked. 239 240 See the description of the 'F' flag in the kernel docs 241 for more details. 242 ''; 243 type = types.bool; 244 }; 245 246 wrapInterpreterInShell = mkOption { 247 default = true; 248 description = lib.mdDoc '' 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 = lib.mdDoc '' 260 Path of the interpreter to expose in the build sandbox. 261 ''; 262 type = types.nullOr types.path; 263 }; 264 }; 265 })); 266 }; 267 268 emulatedSystems = mkOption { 269 default = []; 270 example = [ "wasm32-wasi" "x86_64-windows" "aarch64-linux" ]; 271 description = lib.mdDoc '' 272 List of systems to emulate. Will also configure Nix to 273 support your new systems. 274 Warning: the builder can execute all emulated systems within the same build, which introduces impurities in the case of cross compilation. 275 ''; 276 type = types.listOf types.str; 277 }; 278 }; 279 }; 280 281 config = { 282 boot.binfmt.registrations = builtins.listToAttrs (map (system: { 283 name = system; 284 value = let 285 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); 301 } // (magics.${system} or (throw "Cannot create binfmt registration for system ${system}")); 302 }) cfg.emulatedSystems); 303 nix.settings = lib.mkIf (cfg.emulatedSystems != []) { 304 extra-platforms = cfg.emulatedSystems ++ lib.optional pkgs.stdenv.hostPlatform.isx86_64 "i686-linux"; 305 extra-sandbox-paths = let 306 ruleFor = system: cfg.registrations.${system}; 307 hasWrappedRule = lib.any (system: (ruleFor system).wrapInterpreterInShell) cfg.emulatedSystems; 308 in [ "/run/binfmt" ] 309 ++ lib.optional hasWrappedRule "${pkgs.bash}" 310 ++ (map (system: (ruleFor system).interpreterSandboxPath) cfg.emulatedSystems); 311 }; 312 313 environment.etc."binfmt.d/nixos.conf".source = builtins.toFile "binfmt_nixos.conf" 314 (lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine config.boot.binfmt.registrations)); 315 system.activationScripts.binfmt = stringAfter [ "specialfs" ] '' 316 mkdir -p -m 0755 /run/binfmt 317 ${lib.concatStringsSep "\n" (lib.mapAttrsToList activationSnippet config.boot.binfmt.registrations)} 318 ''; 319 systemd.additionalUpstreamSystemUnits = lib.mkIf (config.boot.binfmt.registrations != {}) [ 320 "proc-sys-fs-binfmt_misc.automount" 321 "proc-sys-fs-binfmt_misc.mount" 322 "systemd-binfmt.service" 323 ]; 324 systemd.services.systemd-binfmt.restartTriggers = [ (builtins.toJSON config.boot.binfmt.registrations) ]; 325 }; 326}