at 23.11-pre 14 kB view raw
1{ config, lib, pkgs, ... }: 2let 3 inherit (lib) mkOption mkDefault 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 loongarch64-linux = { 129 magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02\x01''; 130 mask = ''\xff\xff\xff\xff\xff\xff\xff\xfc\x00\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 131 }; 132 wasm32-wasi = { 133 magicOrExtension = ''\x00asm''; 134 mask = ''\xff\xff\xff\xff''; 135 }; 136 wasm64-wasi = { 137 magicOrExtension = ''\x00asm''; 138 mask = ''\xff\xff\xff\xff''; 139 }; 140 x86_64-windows = { 141 magicOrExtension = "exe"; 142 recognitionType = "extension"; 143 }; 144 i686-windows = { 145 magicOrExtension = "exe"; 146 recognitionType = "extension"; 147 }; 148 }; 149 150in { 151 imports = [ 152 (lib.mkRenamedOptionModule [ "boot" "binfmtMiscRegistrations" ] [ "boot" "binfmt" "registrations" ]) 153 ]; 154 155 options = { 156 boot.binfmt = { 157 registrations = mkOption { 158 default = {}; 159 160 description = lib.mdDoc '' 161 Extra binary formats to register with the kernel. 162 See https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html for more details. 163 ''; 164 165 type = types.attrsOf (types.submodule ({ config, ... }: { 166 options = { 167 recognitionType = mkOption { 168 default = "magic"; 169 description = lib.mdDoc "Whether to recognize executables by magic number or extension."; 170 type = types.enum [ "magic" "extension" ]; 171 }; 172 173 offset = mkOption { 174 default = null; 175 description = lib.mdDoc "The byte offset of the magic number used for recognition."; 176 type = types.nullOr types.int; 177 }; 178 179 magicOrExtension = mkOption { 180 description = lib.mdDoc "The magic number or extension to match on."; 181 type = types.str; 182 }; 183 184 mask = mkOption { 185 default = null; 186 description = 187 lib.mdDoc "A mask to be ANDed with the byte sequence of the file before matching"; 188 type = types.nullOr types.str; 189 }; 190 191 interpreter = mkOption { 192 description = lib.mdDoc '' 193 The interpreter to invoke to run the program. 194 195 Note that the actual registration will point to 196 /run/binfmt/''${name}, so the kernel interpreter length 197 limit doesn't apply. 198 ''; 199 type = types.path; 200 }; 201 202 preserveArgvZero = mkOption { 203 default = false; 204 description = lib.mdDoc '' 205 Whether to pass the original argv[0] to the interpreter. 206 207 See the description of the 'P' flag in the kernel docs 208 for more details; 209 ''; 210 type = types.bool; 211 }; 212 213 openBinary = mkOption { 214 default = config.matchCredentials; 215 description = lib.mdDoc '' 216 Whether to pass the binary to the interpreter as an open 217 file descriptor, instead of a path. 218 ''; 219 type = types.bool; 220 }; 221 222 matchCredentials = mkOption { 223 default = false; 224 description = lib.mdDoc '' 225 Whether to launch with the credentials and security 226 token of the binary, not the interpreter (e.g. setuid 227 bit). 228 229 See the description of the 'C' flag in the kernel docs 230 for more details. 231 232 Implies/requires openBinary = true. 233 ''; 234 type = types.bool; 235 }; 236 237 fixBinary = mkOption { 238 default = false; 239 description = lib.mdDoc '' 240 Whether to open the interpreter file as soon as the 241 registration is loaded, rather than waiting for a 242 relevant file to be invoked. 243 244 See the description of the 'F' flag in the kernel docs 245 for more details. 246 ''; 247 type = types.bool; 248 }; 249 250 wrapInterpreterInShell = mkOption { 251 default = true; 252 description = lib.mdDoc '' 253 Whether to wrap the interpreter in a shell script. 254 255 This allows a shell command to be set as the interpreter. 256 ''; 257 type = types.bool; 258 }; 259 260 interpreterSandboxPath = mkOption { 261 internal = true; 262 default = null; 263 description = lib.mdDoc '' 264 Path of the interpreter to expose in the build sandbox. 265 ''; 266 type = types.nullOr types.path; 267 }; 268 }; 269 })); 270 }; 271 272 emulatedSystems = mkOption { 273 default = []; 274 example = [ "wasm32-wasi" "x86_64-windows" "aarch64-linux" ]; 275 description = lib.mdDoc '' 276 List of systems to emulate. Will also configure Nix to 277 support your new systems. 278 Warning: the builder can execute all emulated systems within the same build, which introduces impurities in the case of cross compilation. 279 ''; 280 type = types.listOf types.str; 281 }; 282 }; 283 }; 284 285 config = { 286 boot.binfmt.registrations = builtins.listToAttrs (map (system: { 287 name = system; 288 value = { config, ... }: let 289 interpreter = getEmulator system; 290 qemuArch = getQemuArch system; 291 292 preserveArgvZero = "qemu-${qemuArch}" == baseNameOf interpreter; 293 interpreterReg = let 294 wrapperName = "qemu-${qemuArch}-binfmt-P"; 295 wrapper = pkgs.wrapQemuBinfmtP wrapperName interpreter; 296 in 297 if preserveArgvZero then "${wrapper}/bin/${wrapperName}" 298 else interpreter; 299 in ({ 300 preserveArgvZero = mkDefault preserveArgvZero; 301 302 interpreter = mkDefault interpreterReg; 303 wrapInterpreterInShell = mkDefault (!config.preserveArgvZero); 304 interpreterSandboxPath = mkDefault (dirOf (dirOf config.interpreter)); 305 } // (magics.${system} or (throw "Cannot create binfmt registration for system ${system}"))); 306 }) cfg.emulatedSystems); 307 nix.settings = lib.mkIf (cfg.emulatedSystems != []) { 308 extra-platforms = cfg.emulatedSystems ++ lib.optional pkgs.stdenv.hostPlatform.isx86_64 "i686-linux"; 309 extra-sandbox-paths = let 310 ruleFor = system: cfg.registrations.${system}; 311 hasWrappedRule = lib.any (system: (ruleFor system).wrapInterpreterInShell) cfg.emulatedSystems; 312 in [ "/run/binfmt" ] 313 ++ lib.optional hasWrappedRule "${pkgs.bash}" 314 ++ (map (system: (ruleFor system).interpreterSandboxPath) cfg.emulatedSystems); 315 }; 316 317 environment.etc."binfmt.d/nixos.conf".source = builtins.toFile "binfmt_nixos.conf" 318 (lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine config.boot.binfmt.registrations)); 319 system.activationScripts.binfmt = stringAfter [ "specialfs" ] '' 320 mkdir -p -m 0755 /run/binfmt 321 ${lib.concatStringsSep "\n" (lib.mapAttrsToList activationSnippet config.boot.binfmt.registrations)} 322 ''; 323 systemd = lib.mkIf (config.boot.binfmt.registrations != {}) { 324 additionalUpstreamSystemUnits = [ 325 "proc-sys-fs-binfmt_misc.automount" 326 "proc-sys-fs-binfmt_misc.mount" 327 "systemd-binfmt.service" 328 ]; 329 services.systemd-binfmt.restartTriggers = [ (builtins.toJSON config.boot.binfmt.registrations) ]; 330 }; 331 }; 332}