]> git.pld-linux.org Git - packages/qemu.git/commitdiff
add -execve patch for qemu-user
authorElan Ruusamäe <glen@delfi.ee>
Mon, 26 Sep 2016 08:16:21 +0000 (11:16 +0300)
committerElan Ruusamäe <glen@delfi.ee>
Mon, 26 Sep 2016 08:43:22 +0000 (11:43 +0300)
https://resin.io/blog/building-arm-containers-on-any-x86-machine-even-dockerhub/

qemu-user-execve.patch [new file with mode: 0644]
qemu.spec

diff --git a/qemu-user-execve.patch b/qemu-user-execve.patch
new file mode 100644 (file)
index 0000000..6f89ff3
--- /dev/null
@@ -0,0 +1,251 @@
+https://resin.io/blog/building-arm-containers-on-any-x86-machine-even-dockerhub/
+https://github.com/resin-io/qemu/commit/782e5bb77014ff136f7bb6133a911e5f53e914a7
+
+From 782e5bb77014ff136f7bb6133a911e5f53e914a7 Mon Sep 17 00:00:00 2001
+From: Petros Angelatos <petrosagg@resin.io>
+Date: Thu, 24 Dec 2015 14:43:17 -0800
+Subject: [PATCH] linux-user: add option to intercept execve() syscalls
+
+In order for one to use QEMU user mode emulation under a chroot, it is
+required to use binfmt_misc. This can be avoided by QEMU never doing a
+raw execve() to the host system.
+
+Introduce a new option, -execve, that uses the current QEMU interpreter
+to intercept execve().
+
+qemu_execve() will prepend the interpreter path , similar to what
+binfmt_misc would do, and then pass the modified execve() to the host.
+
+It is necessary to parse hashbang scripts in that function otherwise
+the kernel will try to run the interpreter of a script without QEMU and
+get an invalid exec format error.
+
+Signed-off-by: Petros Angelatos <petrosagg@resin.io>
+---
+ linux-user/main.c    |  36 ++++++++++++++++
+ linux-user/qemu.h    |   1 +
+ linux-user/syscall.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 3 files changed, 153 insertions(+), 1 deletion(-)
+
+diff --git a/linux-user/main.c b/linux-user/main.c
+index 700724e..16cce85 100644
+--- a/linux-user/main.c
++++ b/linux-user/main.c
+@@ -17,6 +17,7 @@
+  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+  */
+ #include "qemu/osdep.h"
++#include <sys/auxv.h>
+ #include <sys/mman.h>
+ #include <sys/syscall.h>
+ #include <sys/resource.h>
+@@ -75,6 +76,7 @@ static void usage(int exitcode);
+ static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
+ const char *qemu_uname_release;
++const char *qemu_execve_path;
+ /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
+    we allocate a bigger stack. Need a better solution, for example
+@@ -3824,6 +3826,38 @@ static void handle_arg_guest_base(const char *arg)
+     have_guest_base = 1;
+ }
++static void handle_arg_execve(const char *arg)
++{
++    const char *execfn;
++    char buf[PATH_MAX];
++    char *ret;
++    int len;
++
++    /* try getauxval() */
++    execfn = (const char *) getauxval(AT_EXECFN);
++
++    if (execfn != 0) {
++        ret = realpath(execfn, buf);
++
++        if (ret != NULL) {
++            qemu_execve_path = strdup(buf);
++            return;
++        }
++    }
++
++    /* try /proc/self/exe */
++    len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
++
++    if (len != -1) {
++        buf[len] = '\0';
++        qemu_execve_path = strdup(buf);
++        return;
++    }
++
++    fprintf(stderr, "qemu_execve: unable to determine intepreter's path\n");
++    exit(EXIT_FAILURE);
++}
++
+ static void handle_arg_reserved_va(const char *arg)
+ {
+     char *p;
+@@ -3909,6 +3943,8 @@ static const struct qemu_argument arg_table[] = {
+      "uname",      "set qemu uname release string to 'uname'"},
+     {"B",          "QEMU_GUEST_BASE",  true,  handle_arg_guest_base,
+      "address",    "set guest_base address to 'address'"},
++    {"execve",     "QEMU_EXECVE",      false, handle_arg_execve,
++     "",           "use this interpreter when a process calls execve()"},
+     {"R",          "QEMU_RESERVED_VA", true,  handle_arg_reserved_va,
+      "size",       "reserve 'size' bytes for guest virtual address space"},
+     {"d",          "QEMU_LOG",         true,  handle_arg_log,
+diff --git a/linux-user/qemu.h b/linux-user/qemu.h
+index 26b0ba2..8270268 100644
+--- a/linux-user/qemu.h
++++ b/linux-user/qemu.h
+@@ -137,6 +137,7 @@ void init_task_state(TaskState *ts);
+ void task_settid(TaskState *);
+ void stop_all_tasks(void);
+ extern const char *qemu_uname_release;
++extern const char *qemu_execve_path;
+ extern unsigned long mmap_min_addr;
+ /* ??? See if we can avoid exposing so much of the loader internals.  */
+diff --git a/linux-user/syscall.c b/linux-user/syscall.c
+index 9517531..66446f7 100644
+--- a/linux-user/syscall.c
++++ b/linux-user/syscall.c
+@@ -99,6 +99,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
+ #include <linux/route.h>
+ #include <linux/filter.h>
+ #include <linux/blkpg.h>
++#include <linux/binfmts.h>
+ #include "linux_loop.h"
+ #include "uname.h"
+@@ -5845,6 +5846,118 @@ static target_timer_t get_timer_id(abi_long arg)
+     return timerid;
+ }
++/* qemu_execve() Must return target values and target errnos. */
++static abi_long qemu_execve(char *filename, char *argv[],
++                  char *envp[])
++{
++    char *i_arg = NULL, *i_name = NULL;
++    char **new_argp;
++    int argc, fd, ret, i, offset = 3;
++    char *cp;
++    char buf[BINPRM_BUF_SIZE];
++
++    /* normal execve case */
++    if (qemu_execve_path == NULL || *qemu_execve_path == 0) {
++        return get_errno(execve(filename, argv, envp));
++    }
++
++    for (argc = 0; argv[argc] != NULL; argc++) {
++        /* nothing */ ;
++    }
++
++    fd = open(filename, O_RDONLY);
++    if (fd == -1) {
++        return get_errno(fd);
++    }
++
++    ret = read(fd, buf, BINPRM_BUF_SIZE);
++    if (ret == -1) {
++        close(fd);
++        return get_errno(ret);
++    }
++
++    /* if we have less than 2 bytes, we can guess it is not executable */
++    if (ret < 2) {
++        close(fd);
++        return -host_to_target_errno(ENOEXEC);
++    }
++
++    close(fd);
++
++    /* adapted from the kernel
++     * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_script.c
++     */
++    if ((buf[0] == '#') && (buf[1] == '!')) {
++        /*
++         * This section does the #! interpretation.
++         * Sorta complicated, but hopefully it will work.  -TYT
++         */
++
++        buf[BINPRM_BUF_SIZE - 1] = '\0';
++        cp = strchr(buf, '\n');
++        if (cp == NULL) {
++            cp = buf + BINPRM_BUF_SIZE - 1;
++        }
++        *cp = '\0';
++        while (cp > buf) {
++            cp--;
++            if ((*cp == ' ') || (*cp == '\t')) {
++                *cp = '\0';
++            } else {
++                break;
++            }
++        }
++        for (cp = buf + 2; (*cp == ' ') || (*cp == '\t'); cp++) {
++            /* nothing */ ;
++        }
++        if (*cp == '\0') {
++            return -ENOEXEC; /* No interpreter name found */
++        }
++        i_name = cp;
++        i_arg = NULL;
++        for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
++            /* nothing */ ;
++        }
++        while ((*cp == ' ') || (*cp == '\t')) {
++            *cp++ = '\0';
++        }
++        if (*cp) {
++            i_arg = cp;
++        }
++
++        if (i_arg) {
++            offset = 5;
++        } else {
++            offset = 4;
++        }
++    }
++
++    new_argp = alloca((argc + offset + 1) * sizeof(void *));
++
++    /* Copy the original arguments with offset */
++    for (i = 0; i < argc; i++) {
++        new_argp[i + offset] = argv[i];
++    }
++
++    new_argp[0] = strdup(qemu_execve_path);
++    new_argp[1] = strdup("-0");
++    new_argp[offset] = filename;
++    new_argp[argc + offset] = NULL;
++
++    if (i_name) {
++        new_argp[2] = i_name;
++        new_argp[3] = i_name;
++
++        if (i_arg) {
++            new_argp[4] = i_arg;
++        }
++    } else {
++        new_argp[2] = argv[0];
++    }
++
++    return get_errno(execve(qemu_execve_path, new_argp, envp));
++}
++
+ /* do_syscall() should always have a single exit point at the end so
+    that actions, such as logging of syscall results, can be performed.
+    All errnos that do_syscall() returns must be -TARGET_<errcode>. */
+@@ -6104,7 +6217,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
+             if (!(p = lock_user_string(arg1)))
+                 goto execve_efault;
+-            ret = get_errno(execve(p, argp, envp));
++
++            ret = qemu_execve(p, argp, envp);
++
+             unlock_user(p, arg1, 0);
+             goto execve_end;
index 0adb0dd488ec400cbb6bf11aab863b04fb221fff..93df78724f19003946d72ab571e94c935e5077fb 100644 (file)
--- a/qemu.spec
+++ b/qemu.spec
@@ -60,7 +60,7 @@ Source12:     99-%{name}-guest-agent.rules
 Patch0:                %{name}-cflags.patch
 Patch1:                vgabios-widescreens.patch
 Patch2:                %{name}-whitelist.patch
-
+Patch3:                %{name}-user-execve.patch
 Patch4:                %{name}-xattr.patch
 Patch5:                libjpeg-boolean.patch
 Patch6:                x32.patch
@@ -746,7 +746,7 @@ Moduł QEMU dla urządeń blokowych typu 'ssh'.
 %patch0 -p1
 %patch1 -p1
 %patch2 -p0
-
+%patch3 -p1
 %patch4 -p1
 %patch5 -p1
 %patch6 -p1
This page took 0.108792 seconds and 4 git commands to generate.