Patchwork linux-user: add binfmt wrapper for argv[0] handling

login
register
mail settings
Submitter Alexander Graf
Date Sept. 30, 2011, 5:46 p.m.
Message ID <1317404788-30244-1-git-send-email-agraf@suse.de>
Download mbox | patch
Permalink /patch/117176/
State New
Headers show

Comments

Alexander Graf - Sept. 30, 2011, 5:46 p.m.
When using qemu's linux-user binaries through binfmt, argv[0] gets lost
along the execution because qemu only gets passed in the full file name
to the executable while argv[0] can be something completely different.

This breaks in some subtile situations, such as the grep and make test
suites.

This patch adds a wrapper binary called qemu-$TARGET-binfmt that can be
used with binfmt's P flag which passes the full path _and_ argv[0] to
the binfmt handler.

The binary would be smart enough to be versatile and only exist in the
system once, creating the qemu binary path names from its own argv[0].
However, this seemed like it didn't fit the make system too well, so
we're currently creating a new binary for each target archictecture.

CC: Reinhard Max <max@suse.de>
Signed-off-by: Alexander Graf <agraf@suse.de>
---
 Makefile.target             |    8 ++++++++
 linux-user/binfmt.c         |   42 ++++++++++++++++++++++++++++++++++++++++++
 scripts/qemu-binfmt-conf.sh |   34 +++++++++++++++++-----------------
 3 files changed, 67 insertions(+), 17 deletions(-)
 create mode 100644 linux-user/binfmt.c
Alexander Graf - Oct. 29, 2011, 6:08 p.m.
On 30.09.2011, at 19:46, Alexander Graf wrote:

> When using qemu's linux-user binaries through binfmt, argv[0] gets lost
> along the execution because qemu only gets passed in the full file name
> to the executable while argv[0] can be something completely different.
> 
> This breaks in some subtile situations, such as the grep and make test
> suites.
> 
> This patch adds a wrapper binary called qemu-$TARGET-binfmt that can be
> used with binfmt's P flag which passes the full path _and_ argv[0] to
> the binfmt handler.
> 
> The binary would be smart enough to be versatile and only exist in the
> system once, creating the qemu binary path names from its own argv[0].
> However, this seemed like it didn't fit the make system too well, so
> we're currently creating a new binary for each target archictecture.
> 
> CC: Reinhard Max <max@suse.de>
> Signed-off-by: Alexander Graf <agraf@suse.de>

Ping?

Alex
Riku Voipio - Oct. 31, 2011, 7:16 p.m.
On Sat, Oct 29, 2011 at 08:08:39PM +0200, Alexander Graf wrote:
> > When using qemu's linux-user binaries through binfmt, argv[0] gets lost
> > along the execution because qemu only gets passed in the full file name
> > to the executable while argv[0] can be something completely different.
> > 
> > This breaks in some subtile situations, such as the grep and make test
> > suites.
> > 
> > This patch adds a wrapper binary called qemu-$TARGET-binfmt that can be
> > used with binfmt's P flag which passes the full path _and_ argv[0] to
> > the binfmt handler.
> > 
> > The binary would be smart enough to be versatile and only exist in the
> > system once, creating the qemu binary path names from its own argv[0].
> > However, this seemed like it didn't fit the make system too well, so
> > we're currently creating a new binary for each target archictecture.
> > 
> > CC: Reinhard Max <max@suse.de>
> > Signed-off-by: Alexander Graf <agraf@suse.de>
 
> Ping?

Last time a wrapper for binfmt was suggested on this list, it was shot down
since people didn't want to add extra binary to the chroot. But your point
is valid, without proper argv[0] things break sometimes. For the same reason
scratchbox has a wrapper binary instead of calling qemu directly...

Riku
Alexander Graf - Oct. 31, 2011, 11:55 p.m.
On 31.10.2011, at 12:16, Riku Voipio <riku.voipio@iki.fi> wrote:

> On Sat, Oct 29, 2011 at 08:08:39PM +0200, Alexander Graf wrote:
>>> When using qemu's linux-user binaries through binfmt, argv[0] gets lost
>>> along the execution because qemu only gets passed in the full file name
>>> to the executable while argv[0] can be something completely different.
>>> 
>>> This breaks in some subtile situations, such as the grep and make test
>>> suites.
>>> 
>>> This patch adds a wrapper binary called qemu-$TARGET-binfmt that can be
>>> used with binfmt's P flag which passes the full path _and_ argv[0] to
>>> the binfmt handler.
>>> 
>>> The binary would be smart enough to be versatile and only exist in the
>>> system once, creating the qemu binary path names from its own argv[0].
>>> However, this seemed like it didn't fit the make system too well, so
>>> we're currently creating a new binary for each target archictecture.
>>> 
>>> CC: Reinhard Max <max@suse.de>
>>> Signed-off-by: Alexander Graf <agraf@suse.de>
> 
>> Ping?
> 
> Last time a wrapper for binfmt was suggested on this list, it was shot down
> since people didn't want to add extra binary to the chroot. But your point
> is valid, without proper argv[0] things break sometimes. For the same reason
> scratchbox has a wrapper binary instead of calling qemu directly...

Yup. And I really don't want to have downstreams diverge because we're not pragmatic enough :).

Alex

>

Patch

diff --git a/Makefile.target b/Makefile.target
index 88d2f1f..33f9630 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -33,6 +33,10 @@  endif
 PROGS=$(QEMU_PROG)
 STPFILES=
 
+ifdef CONFIG_LINUX_USER
+PROGS+=$(QEMU_PROG)-binfmt
+endif
+
 ifndef CONFIG_HAIKU
 LIBS+=-lm
 endif
@@ -128,6 +132,8 @@  obj-y += $(addprefix ../libuser/, $(user-obj-y))
 obj-y += $(addprefix ../libdis-user/, $(libdis-y))
 obj-y += $(libobj-y)
 
+obj-binfmt-y += binfmt.o
+
 endif #CONFIG_LINUX_USER
 
 #########################################################
@@ -400,6 +406,8 @@  obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
 $(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)
 	$(call LINK,$^)
 
+$(QEMU_PROG)-binfmt: $(obj-binfmt-y)
+	$(call LINK,$^)
 
 gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh
 	$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES),"  GEN   $(TARGET_DIR)$@")
diff --git a/linux-user/binfmt.c b/linux-user/binfmt.c
new file mode 100644
index 0000000..cd1f513
--- /dev/null
+++ b/linux-user/binfmt.c
@@ -0,0 +1,42 @@ 
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <string.h>
+#include <stdlib.h>
+
+
+int main(int argc, char **argv, char **envp)
+{
+    char *binfmt;
+    char **new_argv;
+
+    /*
+     * Check if our file name ends with -binfmt
+     */
+    binfmt = argv[0] + strlen(argv[0]) - strlen("-binfmt");
+    if (strcmp(binfmt, "-binfmt")) {
+        fprintf(stderr, "%s: Invalid executable name\n", argv[0]);
+        exit(1);
+    }
+    if (argc < 3) {
+        fprintf(stderr, "%s: Please use me through binfmt with P flag\n",
+                argv[0]);
+        exit(1);
+    }
+
+    binfmt[0] = '\0';
+    /* Now argv[0] is the real qemu binary name */
+
+    new_argv = (char **)malloc((argc + 2) * sizeof(*new_argv));
+    if (argc > 3) {
+        memcpy(&new_argv[4], &argv[3], (argc - 3) * sizeof(*new_argv));
+    }
+    new_argv[0] = argv[0];
+    new_argv[1] = (char *)"-0";
+    new_argv[2] = argv[2];
+    new_argv[3] = argv[1];
+    new_argv[argc + 1] = NULL;
+
+    return execve(new_argv[0], new_argv, envp);
+}
diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh
index 83a44d8..c164569 100644
--- a/scripts/qemu-binfmt-conf.sh
+++ b/scripts/qemu-binfmt-conf.sh
@@ -31,38 +31,38 @@  esac
 
 # register the interpreter for each cpu except for the native one
 if [ $cpu != "i386" ] ; then
-    echo ':i386:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register
-    echo ':i486:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register
+    echo ':i386:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386-binfmt:P' > /proc/sys/fs/binfmt_misc/register
+    echo ':i486:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386-binfmt:P' > /proc/sys/fs/binfmt_misc/register
 fi
 if [ $cpu != "alpha" ] ; then
-    echo ':alpha:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-alpha:' > /proc/sys/fs/binfmt_misc/register
+    echo ':alpha:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-alpha-binfmt:P' > /proc/sys/fs/binfmt_misc/register
 fi
 if [ $cpu != "arm" ] ; then
-    echo   ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-arm:' > /proc/sys/fs/binfmt_misc/register
-    echo   ':armeb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-armeb:' > /proc/sys/fs/binfmt_misc/register
+    echo   ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-arm-binfmt:P' > /proc/sys/fs/binfmt_misc/register
+    echo   ':armeb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-armeb-binfmt:P' > /proc/sys/fs/binfmt_misc/register
 fi
 if [ $cpu != "sparc" ] ; then
-    echo   ':sparc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-sparc:' > /proc/sys/fs/binfmt_misc/register
+    echo   ':sparc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-sparc-binfmt:P' > /proc/sys/fs/binfmt_misc/register
 fi
 if [ $cpu != "ppc" ] ; then
-    echo   ':ppc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-ppc:' > /proc/sys/fs/binfmt_misc/register
+    echo   ':ppc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-ppc-binfmt:P' > /proc/sys/fs/binfmt_misc/register
 fi
 if [ $cpu != "m68k" ] ; then
     echo   'Please check cpu value and header information for m68k!'
-    echo   ':m68k:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x04:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-m68k:' > /proc/sys/fs/binfmt_misc/register
+    echo   ':m68k:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x04:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-m68k-binfmt:P' > /proc/sys/fs/binfmt_misc/register
 fi
 if [ $cpu != "mips" ] ; then
     # FIXME: We could use the other endianness on a MIPS host.
-    echo   ':mips:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mips:' > /proc/sys/fs/binfmt_misc/register
-    echo   ':mipsel:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mipsel:' > /proc/sys/fs/binfmt_misc/register
-    echo   ':mipsn32:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mipsn32:' > /proc/sys/fs/binfmt_misc/register
-    echo   ':mipsn32el:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mipsn32el:' > /proc/sys/fs/binfmt_misc/register
-    echo   ':mips64:M::\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mips64:' > /proc/sys/fs/binfmt_misc/register
-    echo   ':mips64el:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mips64el:' > /proc/sys/fs/binfmt_misc/register
+    echo   ':mips:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mips-binfmt:P' > /proc/sys/fs/binfmt_misc/register
+    echo   ':mipsel:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mipsel-binfmt:P' > /proc/sys/fs/binfmt_misc/register
+    echo   ':mipsn32:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mipsn32-binfmt:P' > /proc/sys/fs/binfmt_misc/register
+    echo   ':mipsn32el:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mipsn32el-binfmt:P' > /proc/sys/fs/binfmt_misc/register
+    echo   ':mips64:M::\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mips64-binfmt:P' > /proc/sys/fs/binfmt_misc/register
+    echo   ':mips64el:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mips64el-binfmt:P' > /proc/sys/fs/binfmt_misc/register
 fi
 if [ $cpu != "sh" ] ; then
-    echo    ':sh4:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a\x00:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-sh4:' > /proc/sys/fs/binfmt_misc/register
-    echo    ':sh4eb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-sh4eb:' > /proc/sys/fs/binfmt_misc/register
+    echo    ':sh4:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a\x00:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-sh4-binfmt:P' > /proc/sys/fs/binfmt_misc/register
+    echo    ':sh4eb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-sh4eb-binfmt:P' > /proc/sys/fs/binfmt_misc/register
 if [ $cpu != "s390x" ] ; then
-    echo   ':s390x:M::\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x16:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-s390x:' > /proc/sys/fs/binfmt_misc/register
+    echo   ':s390x:M::\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x16:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-s390x-binfmt:P' > /proc/sys/fs/binfmt_misc/register
 fi