at 25.11-pre 17 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 inherit (lib) 9 mkOption 10 mkDefault 11 types 12 optionalString 13 ; 14 15 cfg = config.boot.binfmt; 16 17 makeBinfmtLine = 18 name: 19 { 20 recognitionType, 21 offset, 22 magicOrExtension, 23 mask, 24 preserveArgvZero, 25 openBinary, 26 matchCredentials, 27 fixBinary, 28 ... 29 }: 30 let 31 type = if recognitionType == "magic" then "M" else "E"; 32 offset' = toString offset; 33 mask' = toString mask; 34 interpreter = "/run/binfmt/${name}"; 35 flags = 36 if !(matchCredentials -> openBinary) then 37 throw "boot.binfmt.registrations.${name}: you can't specify openBinary = false when matchCredentials = true." 38 else 39 optionalString preserveArgvZero "P" 40 + optionalString (openBinary && !matchCredentials) "O" 41 + optionalString matchCredentials "C" 42 + optionalString fixBinary "F"; 43 in 44 ":${name}:${type}:${offset'}:${magicOrExtension}:${mask'}:${interpreter}:${flags}"; 45 46 mkInterpreter = 47 name: 48 { interpreter, wrapInterpreterInShell, ... }: 49 if wrapInterpreterInShell then 50 pkgs.writeShellScript "${name}-interpreter" '' 51 #!${pkgs.bash}/bin/sh 52 exec -- ${interpreter} "$@" 53 '' 54 else 55 interpreter; 56 57 # Mapping of systems to “magicOrExtension” and “mask”. Mostly taken from: 58 # - https://github.com/cleverca22/nixos-configs/blob/master/qemu.nix 59 # and 60 # - https://github.com/qemu/qemu/blob/master/scripts/qemu-binfmt-conf.sh 61 # TODO: maybe put these in a JSON file? 62 magics = { 63 armv6l-linux = { 64 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00''; 65 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff''; 66 }; 67 armv7l-linux = { 68 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00''; 69 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff''; 70 }; 71 aarch64-linux = { 72 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00''; 73 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff''; 74 }; 75 aarch64_be-linux = { 76 magicOrExtension = ''\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7''; 77 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 78 }; 79 i386-linux = { 80 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00''; 81 mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 82 }; 83 i486-linux = { 84 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00''; 85 mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 86 }; 87 i586-linux = { 88 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00''; 89 mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 90 }; 91 i686-linux = { 92 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00''; 93 mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 94 }; 95 x86_64-linux = { 96 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00''; 97 mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 98 }; 99 alpha-linux = { 100 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90''; 101 mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 102 }; 103 sparc64-linux = { 104 magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02''; 105 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 106 }; 107 sparc-linux = { 108 magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x12''; 109 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 110 }; 111 powerpc-linux = { 112 magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14''; 113 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 114 }; 115 powerpc64-linux = { 116 magicOrExtension = ''\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15''; 117 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 118 }; 119 powerpc64le-linux = { 120 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x15\x00''; 121 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\x00''; 122 }; 123 mips-linux = { 124 magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00''; 125 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20''; 126 }; 127 mipsel-linux = { 128 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00''; 129 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00''; 130 }; 131 mips64-linux = { 132 magicOrExtension = ''\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08''; 133 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 134 }; 135 mips64el-linux = { 136 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00''; 137 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 138 }; 139 mips64-linuxabin32 = { 140 magicOrExtension = ''\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20''; 141 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20''; 142 }; 143 mips64el-linuxabin32 = { 144 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00''; 145 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00''; 146 }; 147 riscv32-linux = { 148 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00''; 149 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 150 }; 151 riscv64-linux = { 152 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00''; 153 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 154 }; 155 loongarch64-linux = { 156 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02\x01''; 157 mask = ''\xff\xff\xff\xff\xff\xff\xff\xfc\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 158 }; 159 wasm32-wasi = { 160 magicOrExtension = ''\x00asm''; 161 mask = ''\xff\xff\xff\xff''; 162 }; 163 wasm64-wasi = { 164 magicOrExtension = ''\x00asm''; 165 mask = ''\xff\xff\xff\xff''; 166 }; 167 s390x-linux = { 168 magicOrExtension = ''\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x16''; 169 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 170 }; 171 x86_64-windows.magicOrExtension = "MZ"; 172 i686-windows.magicOrExtension = "MZ"; 173 }; 174 175in 176{ 177 imports = [ 178 (lib.mkRenamedOptionModule [ "boot" "binfmtMiscRegistrations" ] [ "boot" "binfmt" "registrations" ]) 179 ]; 180 181 options = { 182 boot.binfmt = { 183 registrations = mkOption { 184 default = { }; 185 186 description = '' 187 Extra binary formats to register with the kernel. 188 See https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html for more details. 189 ''; 190 191 type = types.attrsOf ( 192 types.submodule ( 193 { config, ... }: 194 { 195 options = { 196 recognitionType = mkOption { 197 default = "magic"; 198 description = "Whether to recognize executables by magic number or extension."; 199 type = types.enum [ 200 "magic" 201 "extension" 202 ]; 203 }; 204 205 offset = mkOption { 206 default = null; 207 description = "The byte offset of the magic number used for recognition."; 208 type = types.nullOr types.int; 209 }; 210 211 magicOrExtension = mkOption { 212 description = "The magic number or extension to match on."; 213 type = types.str; 214 }; 215 216 mask = mkOption { 217 default = null; 218 description = "A mask to be ANDed with the byte sequence of the file before matching"; 219 type = types.nullOr types.str; 220 }; 221 222 interpreter = mkOption { 223 description = '' 224 The interpreter to invoke to run the program. 225 226 Note that the actual registration will point to 227 /run/binfmt/''${name}, so the kernel interpreter length 228 limit doesn't apply. 229 ''; 230 type = types.path; 231 }; 232 233 preserveArgvZero = mkOption { 234 default = false; 235 description = '' 236 Whether to pass the original argv[0] to the interpreter. 237 238 See the description of the 'P' flag in the kernel docs 239 for more details; 240 ''; 241 type = types.bool; 242 }; 243 244 openBinary = mkOption { 245 default = config.matchCredentials; 246 description = '' 247 Whether to pass the binary to the interpreter as an open 248 file descriptor, instead of a path. 249 ''; 250 type = types.bool; 251 }; 252 253 matchCredentials = mkOption { 254 default = false; 255 description = '' 256 Whether to launch with the credentials and security 257 token of the binary, not the interpreter (e.g. setuid 258 bit). 259 260 See the description of the 'C' flag in the kernel docs 261 for more details. 262 263 Implies/requires openBinary = true. 264 ''; 265 type = types.bool; 266 }; 267 268 fixBinary = mkOption { 269 default = false; 270 description = '' 271 Whether to open the interpreter file as soon as the 272 registration is loaded, rather than waiting for a 273 relevant file to be invoked. 274 275 See the description of the 'F' flag in the kernel docs 276 for more details. 277 ''; 278 type = types.bool; 279 }; 280 281 wrapInterpreterInShell = mkOption { 282 default = true; 283 description = '' 284 Whether to wrap the interpreter in a shell script. 285 286 This allows a shell command to be set as the interpreter. 287 ''; 288 type = types.bool; 289 }; 290 291 interpreterSandboxPath = mkOption { 292 internal = true; 293 default = null; 294 description = '' 295 Path of the interpreter to expose in the build sandbox. 296 ''; 297 type = types.nullOr types.path; 298 }; 299 }; 300 } 301 ) 302 ); 303 }; 304 305 emulatedSystems = mkOption { 306 default = [ ]; 307 example = [ 308 "wasm32-wasi" 309 "x86_64-windows" 310 "aarch64-linux" 311 ]; 312 description = '' 313 List of systems to emulate. Will also configure Nix to 314 support your new systems. 315 Warning: the builder can execute all emulated systems within the same build, which introduces impurities in the case of cross compilation. 316 ''; 317 type = types.listOf (types.enum (builtins.attrNames magics)); 318 }; 319 320 addEmulatedSystemsToNixSandbox = mkOption { 321 type = types.bool; 322 default = true; 323 example = false; 324 description = '' 325 Whether to add the {option}`boot.binfmt.emulatedSystems` to {option}`nix.settings.extra-platforms`. 326 Disable this to use remote builders for those platforms, while allowing testing binaries locally. 327 ''; 328 }; 329 330 preferStaticEmulators = mkOption { 331 default = false; 332 description = '' 333 Whether to use static emulators when available. 334 335 This enables the kernel to preload the emulator binaries when 336 the binfmt registrations are added, obviating the need to make 337 the emulator binaries available inside chroots and chroot-like 338 sandboxes. 339 ''; 340 type = types.bool; 341 }; 342 }; 343 }; 344 345 config = { 346 assertions = lib.mapAttrsToList (name: reg: { 347 assertion = reg.fixBinary -> !reg.wrapInterpreterInShell; 348 message = "boot.binfmt.registrations.\"${name}\" cannot have fixBinary when the interpreter is invoked through a shell."; 349 }) cfg.registrations; 350 351 boot.binfmt.registrations = builtins.listToAttrs ( 352 map ( 353 system: 354 assert system != pkgs.stdenv.hostPlatform.system; 355 { 356 name = system; 357 value = 358 { config, ... }: 359 let 360 elaborated = lib.systems.elaborate { inherit system; }; 361 useStaticEmulator = cfg.preferStaticEmulators && elaborated.staticEmulatorAvailable pkgs; 362 interpreter = elaborated.emulator (if useStaticEmulator then pkgs.pkgsStatic else pkgs); 363 364 inherit (elaborated) qemuArch; 365 isQemu = "qemu-${qemuArch}" == baseNameOf interpreter; 366 367 interpreterReg = 368 let 369 wrapperName = "qemu-${qemuArch}-binfmt-P"; 370 wrapper = pkgs.wrapQemuBinfmtP wrapperName interpreter; 371 in 372 if isQemu && !useStaticEmulator then "${wrapper}/bin/${wrapperName}" else interpreter; 373 in 374 ( 375 { 376 preserveArgvZero = mkDefault isQemu; 377 378 interpreter = mkDefault interpreterReg; 379 fixBinary = mkDefault useStaticEmulator; 380 wrapInterpreterInShell = mkDefault (!config.preserveArgvZero && !config.fixBinary); 381 interpreterSandboxPath = mkDefault (dirOf (dirOf config.interpreter)); 382 } 383 // (magics.${system} or (throw "Cannot create binfmt registration for system ${system}")) 384 ); 385 } 386 ) cfg.emulatedSystems 387 ); 388 nix.settings = lib.mkIf (cfg.addEmulatedSystemsToNixSandbox && cfg.emulatedSystems != [ ]) { 389 extra-platforms = 390 cfg.emulatedSystems 391 ++ lib.optional pkgs.stdenv.hostPlatform.isx86_64 "i686-linux"; 392 extra-sandbox-paths = 393 let 394 ruleFor = system: cfg.registrations.${system}; 395 hasWrappedRule = lib.any (system: (ruleFor system).wrapInterpreterInShell) cfg.emulatedSystems; 396 in 397 [ "/run/binfmt" ] 398 ++ lib.optional hasWrappedRule "${pkgs.bash}" 399 ++ (map (system: (ruleFor system).interpreterSandboxPath) cfg.emulatedSystems); 400 }; 401 402 environment.etc."binfmt.d/nixos.conf".source = builtins.toFile "binfmt_nixos.conf" ( 403 lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine config.boot.binfmt.registrations) 404 ); 405 406 systemd = lib.mkMerge [ 407 ({ 408 tmpfiles.rules = 409 [ 410 "d /run/binfmt 0755 -" 411 ] 412 ++ lib.mapAttrsToList (name: interpreter: "L+ /run/binfmt/${name} - - - - ${interpreter}") ( 413 lib.mapAttrs mkInterpreter config.boot.binfmt.registrations 414 ); 415 }) 416 417 (lib.mkIf (config.boot.binfmt.registrations != { }) { 418 additionalUpstreamSystemUnits = [ 419 "proc-sys-fs-binfmt_misc.automount" 420 "proc-sys-fs-binfmt_misc.mount" 421 "systemd-binfmt.service" 422 ]; 423 services.systemd-binfmt.after = [ "systemd-tmpfiles-setup.service" ]; 424 services.systemd-binfmt.restartTriggers = [ (builtins.toJSON config.boot.binfmt.registrations) ]; 425 }) 426 ]; 427 }; 428}