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