Package: qemu / 1:7.2+dfsg-7+deb12u13

linux-user-binfmt-P.diff Patch series | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
Subject: [PATCH, HACK]: linux-user: handle binfmt-misc P flag as a separate exe name
From: Michael Tokarev <mjt@tls.msk.ru>
Date: Sat, 13 Feb 2021 13:57:52 +0300
Updated: Wed, 31 Aug 2022 12:30:17 +0300

A hackish way to distinguish the case when qemu-user binary is executed
using in-kernel binfmt-misc subsystem with P flag (preserve argv).
We register binfmt interpreter under name /usr/libexec/qemu-binfmt/qemu-foo-binfmt-P
(which is just a symlink to ../../bin/qemu-foo), and if run like that,
qemu-user binary will "know" it should interpret argv[1] & argv[2]
in a special way.

diff --git a/linux-user/main.c b/linux-user/main.c
index e44bdb17b8..587bd02db2 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -562,7 +562,7 @@ static void usage(int exitcode)
     exit(exitcode);
 }
 
-static int parse_args(int argc, char **argv)
+static int parse_args(int argc, char **argv, bool *preserve_argv0)
 {
     const char *r;
     int optind;
@@ -579,6 +579,28 @@ static int parse_args(int argc, char **argv)
         }
     }
 
+    /* HACK alert.
+     * when run as an interpreter using kernel's binfmt-misc mechanism,
+     * we have to know where are we (our own binary), where's the binary being run,
+     * and what it's argv[0] element.
+     * Only with the P interpreter flag kernel passes all 3 elements as first 3 argv[],
+     * but we can't distinguish if we were run with or without this P flag.
+     * So we register a special name with binfmt-misc system, a name which ends up
+     * in "-binfmt-P", and if our argv[0] ends up with that, we assume we were run
+     * from kernel's binfmt with P flag and our first 3 args are from kernel.
+     */
+    if (strlen(argv[0]) > sizeof("binfmt-P") &&
+        strcmp(argv[0] + strlen(argv[0]) - sizeof("binfmt-P"), "-binfmt-P") == 0) {
+        if (argc < 3) {
+            (void) fprintf(stderr, "qemu: %s has to be run using kernel binfmt-misc subsystem\n", argv[0]);
+            exit(EXIT_FAILURE);
+        }
+        exec_path = argv[1];
+        handle_arg_argv0(argv[2]);
+        *preserve_argv0 = true;
+        return 2;
+    }
+
     optind = 1;
     for (;;) {
         if (optind >= argc) {
@@ -648,7 +670,7 @@ int main(int argc, char **argv, char **envp)
     int ret;
     int execfd;
     unsigned long max_reserved_va;
-    bool preserve_argv0;
+    bool preserve_argv0 = 0;
 
     error_init(argv[0]);
     module_call_init(MODULE_INIT_TRACE);
@@ -678,7 +700,7 @@ int main(int argc, char **argv, char **envp)
     qemu_add_opts(&qemu_trace_opts);
     qemu_plugin_add_opts();
 
-    optind = parse_args(argc, argv);
+    optind = parse_args(argc, argv, &preserve_argv0);
 
     qemu_set_log_filename_flags(last_log_filename,
                                 last_log_mask | (enable_strace * LOG_STRACE),
@@ -717,7 +739,9 @@ int main(int argc, char **argv, char **envp)
 
     /*
      * get binfmt_misc flags
+     * but only if not already done by parse_args() above
      */
+    if (!preserve_argv0) {
     preserve_argv0 = !!(qemu_getauxval(AT_FLAGS) & AT_FLAGS_PRESERVE_ARGV0);
 
     /*
@@ -728,6 +752,7 @@ int main(int argc, char **argv, char **envp)
     if (optind + 1 < argc && preserve_argv0) {
         optind++;
     }
+    }
 
     if (cpu_model == NULL) {
         cpu_model = cpu_get_model(get_elf_eflags(execfd));