qemu: Add binfmt preserve-argv[0] wrapper

+112
+79
pkgs/applications/virtualization/qemu/binfmt-p-wrapper.c
··· 1 + // This is a tiny wrapper that converts the extra arv[0] argument 2 + // from binfmt-misc with the P flag enabled to QEMU parameters. 3 + // It also prevents LD_* environment variables from being applied 4 + // to QEMU itself. 5 + 6 + #include <stdio.h> 7 + #include <stdlib.h> 8 + #include <string.h> 9 + #include <unistd.h> 10 + 11 + #ifndef TARGET_QEMU 12 + #error "Define TARGET_QEMU to be the path to the qemu-user binary (e.g., -DTARGET_QEMU=\"/full/path/to/qemu-riscv64\")" 13 + #endif 14 + 15 + extern char **environ; 16 + 17 + int main(int argc, char *argv[]) { 18 + if (argc < 3) { 19 + fprintf(stderr, "%s: This should be run as the binfmt interpreter with the P flag\n", argv[0]); 20 + fprintf(stderr, "%s: My preconfigured qemu-user binary: %s\n", argv[0], TARGET_QEMU); 21 + return 1; 22 + } 23 + 24 + size_t environ_count = 0; 25 + for (char **cur = environ; *cur != NULL; ++cur) { 26 + environ_count++; 27 + } 28 + 29 + size_t new_argc = 3; 30 + size_t new_argv_alloc = argc + 2 * environ_count + 2; // [ "-E", env ] for each LD_* env + [ "-0", argv0 ] 31 + char **new_argv = (char**)malloc((new_argv_alloc + 1) * sizeof(char*)); 32 + if (!new_argv) { 33 + fprintf(stderr, "FATAL: Failed to allocate new argv array\n"); 34 + abort(); 35 + } 36 + 37 + new_argv[0] = TARGET_QEMU; 38 + new_argv[1] = "-0"; 39 + new_argv[2] = argv[2]; 40 + 41 + // Pass all LD_ env variables as -E and strip them in `new_environ` 42 + size_t new_environc = 0; 43 + char **new_environ = (char**)malloc((environ_count + 1) * sizeof(char*)); 44 + if (!new_environ) { 45 + fprintf(stderr, "FATAL: Failed to allocate new environ array\n"); 46 + abort(); 47 + } 48 + 49 + for (char **cur = environ; *cur != NULL; ++cur) { 50 + if (strncmp("LD_", *cur, 3) == 0) { 51 + new_argv[new_argc++] = "-E"; 52 + new_argv[new_argc++] = *cur; 53 + } else { 54 + new_environ[new_environc++] = *cur; 55 + } 56 + } 57 + new_environ[new_environc] = NULL; 58 + 59 + size_t new_arg_start = new_argc; 60 + new_argc += argc - 3 + 2; // [ "--", full_binary_path ] 61 + 62 + if (argc > 3) { 63 + memcpy(&new_argv[new_arg_start + 2], &argv[3], (argc - 3) * sizeof(char**)); 64 + } 65 + 66 + new_argv[new_arg_start] = "--"; 67 + new_argv[new_arg_start + 1] = argv[1]; 68 + new_argv[new_argc] = NULL; 69 + 70 + #ifdef DEBUG 71 + for (size_t i = 0; i < new_argc; ++i) { 72 + fprintf(stderr, "argv[%zu] = %s\n", i, new_argv[i]); 73 + } 74 + #endif 75 + 76 + return execve(new_argv[0], new_argv, new_environ); 77 + } 78 + 79 + // vim: et:ts=4:sw=4
+31
pkgs/applications/virtualization/qemu/binfmt-p-wrapper.nix
··· 1 + # binfmt preserve-argv[0] wrapper 2 + # 3 + # More details in binfmt-p-wrapper.c 4 + # 5 + # The wrapper has to be static so LD_* environment variables 6 + # cannot affect the execution of the wrapper itself. 7 + 8 + { lib, stdenv, pkgsStatic, enableDebug ? false }: 9 + 10 + name: emulator: 11 + 12 + pkgsStatic.stdenv.mkDerivation { 13 + inherit name; 14 + 15 + src = ./binfmt-p-wrapper.c; 16 + 17 + dontUnpack = true; 18 + dontInstall = true; 19 + 20 + buildPhase = '' 21 + runHook preBuild 22 + 23 + mkdir -p $out/bin 24 + $CC -o $out/bin/${name} -static -std=c99 -O2 \ 25 + -DTARGET_QEMU=\"${emulator}\" \ 26 + ${lib.optionalString enableDebug "-DDEBUG"} \ 27 + $src 28 + 29 + runHook postBuild 30 + ''; 31 + }
+2
pkgs/top-level/all-packages.nix
··· 27324 27324 27325 27325 qemu-utils = callPackage ../applications/virtualization/qemu/utils.nix {}; 27326 27326 27327 + wrapQemuBinfmtP = callPackage ../applications/virtualization/qemu/binfmt-p-wrapper.nix { }; 27328 + 27327 27329 qgis-unwrapped = libsForQt5.callPackage ../applications/gis/qgis/unwrapped.nix { 27328 27330 withGrass = false; 27329 27331 };