about summary refs log tree commit diff
path: root/pkgs/applications/virtualization/qemu
diff options
context:
space:
mode:
author0x4A6F <0x4A6F@users.noreply.github.com>2022-01-06 19:21:47 +0100
committerGitHub <noreply@github.com>2022-01-06 19:21:47 +0100
commit29acc14f0db8957855c1e629a105992a4c975100 (patch)
tree7980af52690dc26b93263efcfa53d18253cdf858 /pkgs/applications/virtualization/qemu
parent7a022212c83000b4d56d93df4cd5610caa74e4b9 (diff)
parentc8d137961d29b7ad1e9470718802209b0e636776 (diff)
Merge pull request #143060 from zhaofengli/binfmt-argv0
nixos/binfmt: Add QEMU wrapper to preserve argv[0]
Diffstat (limited to 'pkgs/applications/virtualization/qemu')
-rw-r--r--pkgs/applications/virtualization/qemu/binfmt-p-wrapper.c79
-rw-r--r--pkgs/applications/virtualization/qemu/binfmt-p-wrapper.nix31
2 files changed, 110 insertions, 0 deletions
diff --git a/pkgs/applications/virtualization/qemu/binfmt-p-wrapper.c b/pkgs/applications/virtualization/qemu/binfmt-p-wrapper.c
new file mode 100644
index 0000000000000..f956768862eca
--- /dev/null
+++ b/pkgs/applications/virtualization/qemu/binfmt-p-wrapper.c
@@ -0,0 +1,79 @@
+// This is a tiny wrapper that converts the extra arv[0] argument
+// from binfmt-misc with the P flag enabled to QEMU parameters.
+// It also prevents LD_* environment variables from being applied
+// to QEMU itself.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef TARGET_QEMU
+#error "Define TARGET_QEMU to be the path to the qemu-user binary (e.g., -DTARGET_QEMU=\"/full/path/to/qemu-riscv64\")"
+#endif
+
+extern char **environ;
+
+int main(int argc, char *argv[]) {
+    if (argc < 3) {
+        fprintf(stderr, "%s: This should be run as the binfmt interpreter with the P flag\n", argv[0]);
+        fprintf(stderr, "%s: My preconfigured qemu-user binary: %s\n", argv[0], TARGET_QEMU);
+        return 1;
+    }
+
+    size_t environ_count = 0;
+    for (char **cur = environ; *cur != NULL; ++cur) {
+        environ_count++;
+    }
+
+    size_t new_argc = 3;
+    size_t new_argv_alloc = argc + 2 * environ_count + 2; // [ "-E", env ] for each LD_* env + [ "-0", argv0 ]
+    char **new_argv = (char**)malloc((new_argv_alloc + 1) * sizeof(char*));
+    if (!new_argv) {
+        fprintf(stderr, "FATAL: Failed to allocate new argv array\n");
+        abort();
+    }
+
+    new_argv[0] = TARGET_QEMU;
+    new_argv[1] = "-0";
+    new_argv[2] = argv[2];
+
+    // Pass all LD_ env variables as -E and strip them in `new_environ`
+    size_t new_environc = 0;
+    char **new_environ = (char**)malloc((environ_count + 1) * sizeof(char*));
+    if (!new_environ) {
+        fprintf(stderr, "FATAL: Failed to allocate new environ array\n");
+        abort();
+    }
+
+    for (char **cur = environ; *cur != NULL; ++cur) {
+        if (strncmp("LD_", *cur, 3) == 0) {
+            new_argv[new_argc++] = "-E";
+            new_argv[new_argc++] = *cur;
+        } else {
+            new_environ[new_environc++] = *cur;
+        }
+    }
+    new_environ[new_environc] = NULL;
+
+    size_t new_arg_start = new_argc;
+    new_argc += argc - 3 + 2; // [ "--", full_binary_path ]
+
+    if (argc > 3) {
+        memcpy(&new_argv[new_arg_start + 2], &argv[3], (argc - 3) * sizeof(char**));
+    }
+
+    new_argv[new_arg_start] = "--";
+    new_argv[new_arg_start + 1] = argv[1];
+    new_argv[new_argc] = NULL;
+
+#ifdef DEBUG
+    for (size_t i = 0; i < new_argc; ++i) {
+        fprintf(stderr, "argv[%zu] = %s\n", i, new_argv[i]);
+    }
+#endif
+
+    return execve(new_argv[0], new_argv, new_environ);
+}
+
+// vim: et:ts=4:sw=4
diff --git a/pkgs/applications/virtualization/qemu/binfmt-p-wrapper.nix b/pkgs/applications/virtualization/qemu/binfmt-p-wrapper.nix
new file mode 100644
index 0000000000000..fada14569299b
--- /dev/null
+++ b/pkgs/applications/virtualization/qemu/binfmt-p-wrapper.nix
@@ -0,0 +1,31 @@
+# binfmt preserve-argv[0] wrapper
+#
+# More details in binfmt-p-wrapper.c
+#
+# The wrapper has to be static so LD_* environment variables
+# cannot affect the execution of the wrapper itself.
+
+{ lib, stdenv, pkgsStatic, enableDebug ? false }:
+
+name: emulator:
+
+pkgsStatic.stdenv.mkDerivation {
+  inherit name;
+
+  src = ./binfmt-p-wrapper.c;
+
+  dontUnpack = true;
+  dontInstall = true;
+
+  buildPhase = ''
+    runHook preBuild
+
+    mkdir -p $out/bin
+    $CC -o $out/bin/${name} -static -std=c99 -O2 \
+        -DTARGET_QEMU=\"${emulator}\" \
+        ${lib.optionalString enableDebug "-DDEBUG"} \
+        $src
+
+    runHook postBuild
+  '';
+}