[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
RFC: linux-user: preserving argv[0] of the original binary in context of
From: |
Michael Tokarev |
Subject: |
RFC: linux-user: preserving argv[0] of the original binary in context of binfmt-misc |
Date: |
Sun, 14 Feb 2021 18:25:57 +0300 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.7.1 |
Hi!
As known for a long time, qemu's linux-user, when invoked in context of
binfmt-misc mechanism,
does not preserve the original argv[0] element, so some software which relies
on argv[0] is
not functioning under qemu-user. When run this way, argv[0] of the program
being run under
qemu-user points to this qemu-user binary, instead of being what has been used
to spawn the
original binary.
There's an interpreter flag in binfmt handling in recent kernels, P, or
preserve, which meant
to pass 3 extra first arguments to the binfmt interpeter, - namely, the path to
interpreter
itself, the argv[0] as used when spawning the original binary, and the actual
path of the
said binary. But qemu-user/main does not handle this situation, - it should be
prepared for
this unusual way of being invoked.
There are several hackish solutions exists on this theme used by downstreams,
which introduces
a wrapper program especially for binfmt registration and nothing else, uses
this P flag, and
uses -argv0 qemu-user argument to pass all the necessary information to
qemu-user.
But these wrappers introduce a different issue: since the wrapper executes the
qemu binary,
we can't use F binfmt-misc flag anymore without copying the qemu-user binary
inside any
foreign chroot being used with it.
So the possible solution is to made qemu-user aware of this in-kernel binfmt
mechanism,
which I implemented for Debian for now, as a guinea pig :)
Since the original problem is the proper handling of programs which depend on
their own
name in argv[0], the proposed solution is also based on the program name, -
this time
it is the name under which qemu-user binary is called.
I introduced a special name for qemu-user binaries to be used _only_ for binfmt
registration.
This is, in my case, /usr/libexec/qemu-binfmt/foo-binfmt-P - where "foo" is the
architecture
name, and "-binfmt-P" is the literal suffix. This name is just a (sym)link to
/usr/bin/qemu-foo,
- just an alternative name for qemu-foo, nothing more.
And added a patch for linux-user/main.c which checks suffix of its argv[0], and
if it ends
up on the literal "-binfmt-P", we assume we're being called from in-kernel
binfmt-misc
subsystem with the P flag enabled, which means first 3 args are: our own name,
the original
argv[0], and the intended binary's path, and the rest are the arguments for the
binary to
run.
At first I thought it is a hackish approach, and mentioned that in a comment in
the code,
but the more I think about it, the more I like it, and the more it makes sense.
Here's the patch I used in Debian (it is not intended for upstream for now), -
for comments,
what do you think? At least it seems like a good step in the right direction,
finally.. :)
And we have another issue still, in the same field. Some programs executes
itself by using
/proc/self/exe. This does not work under linux-user too, since this link,
again, points to
the qemu binary, not the original binary being run. But this is a different
story.
Thanks,
/mjt
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
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 24d1eb73ad..5596dab9be 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -560,6 +560,27 @@ 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);
+ }
+ handle_arg_argv0(argv[1]);
+ exec_path = argv[2];
+ return 2;
+ }
+
optind = 1;
for (;;) {
if (optind >= argc) {
- RFC: linux-user: preserving argv[0] of the original binary in context of binfmt-misc,
Michael Tokarev <=