at 22.05-pre 12 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, ... }: '' 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 ''; 31 32 getEmulator = system: (lib.systems.elaborate { inherit system; }).emulator pkgs; 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''; 102 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff''; 103 }; 104 mipsel-linux = { 105 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00''; 106 mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 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\xff\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\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff''; 115 }; 116 riscv32-linux = { 117 magicOrExtension = ''\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\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 riscv64-linux = { 121 magicOrExtension = ''\x7fELF\x02\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 wasm32-wasi = { 125 magicOrExtension = ''\x00asm''; 126 mask = ''\xff\xff\xff\xff''; 127 }; 128 wasm64-wasi = { 129 magicOrExtension = ''\x00asm''; 130 mask = ''\xff\xff\xff\xff''; 131 }; 132 x86_64-windows = { 133 magicOrExtension = ".exe"; 134 recognitionType = "extension"; 135 }; 136 i686-windows = { 137 magicOrExtension = ".exe"; 138 recognitionType = "extension"; 139 }; 140 }; 141 142in { 143 imports = [ 144 (lib.mkRenamedOptionModule [ "boot" "binfmtMiscRegistrations" ] [ "boot" "binfmt" "registrations" ]) 145 ]; 146 147 options = { 148 boot.binfmt = { 149 registrations = mkOption { 150 default = {}; 151 152 description = '' 153 Extra binary formats to register with the kernel. 154 See https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html for more details. 155 ''; 156 157 type = types.attrsOf (types.submodule ({ config, ... }: { 158 options = { 159 recognitionType = mkOption { 160 default = "magic"; 161 description = "Whether to recognize executables by magic number or extension."; 162 type = types.enum [ "magic" "extension" ]; 163 }; 164 165 offset = mkOption { 166 default = null; 167 description = "The byte offset of the magic number used for recognition."; 168 type = types.nullOr types.int; 169 }; 170 171 magicOrExtension = mkOption { 172 description = "The magic number or extension to match on."; 173 type = types.str; 174 }; 175 176 mask = mkOption { 177 default = null; 178 description = 179 "A mask to be ANDed with the byte sequence of the file before matching"; 180 type = types.nullOr types.str; 181 }; 182 183 interpreter = mkOption { 184 description = '' 185 The interpreter to invoke to run the program. 186 187 Note that the actual registration will point to 188 /run/binfmt/''${name}, so the kernel interpreter length 189 limit doesn't apply. 190 ''; 191 type = types.path; 192 }; 193 194 preserveArgvZero = mkOption { 195 default = false; 196 description = '' 197 Whether to pass the original argv[0] to the interpreter. 198 199 See the description of the 'P' flag in the kernel docs 200 for more details; 201 ''; 202 type = types.bool; 203 }; 204 205 openBinary = mkOption { 206 default = config.matchCredentials; 207 description = '' 208 Whether to pass the binary to the interpreter as an open 209 file descriptor, instead of a path. 210 ''; 211 type = types.bool; 212 }; 213 214 matchCredentials = mkOption { 215 default = false; 216 description = '' 217 Whether to launch with the credentials and security 218 token of the binary, not the interpreter (e.g. setuid 219 bit). 220 221 See the description of the 'C' flag in the kernel docs 222 for more details. 223 224 Implies/requires openBinary = true. 225 ''; 226 type = types.bool; 227 }; 228 229 fixBinary = mkOption { 230 default = false; 231 description = '' 232 Whether to open the interpreter file as soon as the 233 registration is loaded, rather than waiting for a 234 relevant file to be invoked. 235 236 See the description of the 'F' flag in the kernel docs 237 for more details. 238 ''; 239 type = types.bool; 240 }; 241 }; 242 })); 243 }; 244 245 emulatedSystems = mkOption { 246 default = []; 247 example = [ "wasm32-wasi" "x86_64-windows" "aarch64-linux" ]; 248 description = '' 249 List of systems to emulate. Will also configure Nix to 250 support your new systems. 251 Warning: the builder can execute all emulated systems within the same build, which introduces impurities in the case of cross compilation. 252 ''; 253 type = types.listOf types.str; 254 }; 255 }; 256 }; 257 258 config = { 259 boot.binfmt.registrations = builtins.listToAttrs (map (system: { 260 name = system; 261 value = { 262 interpreter = getEmulator system; 263 } // (magics.${system} or (throw "Cannot create binfmt registration for system ${system}")); 264 }) cfg.emulatedSystems); 265 # TODO: add a nix.extraPlatforms option to NixOS! 266 nix.extraOptions = lib.mkIf (cfg.emulatedSystems != []) '' 267 extra-platforms = ${toString (cfg.emulatedSystems ++ lib.optional pkgs.stdenv.hostPlatform.isx86_64 "i686-linux")} 268 ''; 269 nix.sandboxPaths = lib.mkIf (cfg.emulatedSystems != []) 270 ([ "/run/binfmt" "${pkgs.bash}" ] ++ (map (system: dirOf (dirOf (getEmulator system))) cfg.emulatedSystems)); 271 272 environment.etc."binfmt.d/nixos.conf".source = builtins.toFile "binfmt_nixos.conf" 273 (lib.concatStringsSep "\n" (lib.mapAttrsToList makeBinfmtLine config.boot.binfmt.registrations)); 274 system.activationScripts.binfmt = stringAfter [ "specialfs" ] '' 275 mkdir -p -m 0755 /run/binfmt 276 ${lib.concatStringsSep "\n" (lib.mapAttrsToList activationSnippet config.boot.binfmt.registrations)} 277 ''; 278 systemd.additionalUpstreamSystemUnits = lib.mkIf (config.boot.binfmt.registrations != {}) [ 279 "proc-sys-fs-binfmt_misc.automount" 280 "proc-sys-fs-binfmt_misc.mount" 281 "systemd-binfmt.service" 282 ]; 283 }; 284}