Le 14/06/2016 à 21:26, Joel Holdsworth a écrit : > From: Petros Angelatos <petros...@resin.io> > > 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 <petros...@resin.io> > Tested-by: Laurent Vivier <laur...@vivier.eu> > Reviewed-by: Laurent Vivier <laur...@vivier.eu> > --- > linux-user/main.c | 37 ++++++++++++++ > linux-user/qemu.h | 1 + > linux-user/syscall.c | 137 > ++++++++++++++++++++++++++++++++++++++++++++++----- > 3 files changed, 164 insertions(+), 11 deletions(-) > > diff --git a/linux-user/main.c b/linux-user/main.c > index f8a8764..429dc06 100644 > --- a/linux-user/main.c > +++ b/linux-user/main.c > @@ -18,6 +18,8 @@ > */ > #include "qemu/osdep.h" > #include "qemu-version.h" > + > +#include <sys/auxv.h> > #include <sys/mman.h> > #include <sys/syscall.h> > #include <sys/resource.h> > @@ -79,6 +81,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 > @@ -3947,6 +3950,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; > @@ -4032,6 +4067,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 56f29c3..567bcc1 100644 > --- a/linux-user/qemu.h > +++ b/linux-user/qemu.h > @@ -149,6 +149,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 71ccbd9..a478f56 100644 > --- a/linux-user/syscall.c > +++ b/linux-user/syscall.c > @@ -106,6 +106,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base, > #include <linux/rtnetlink.h> > #endif > #include <linux/audit.h> > +#include <linux/binfmts.h> > #include "linux_loop.h" > #include "uname.h" > > @@ -6659,6 +6660,128 @@ 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));
You need a safe_execve() here, too. Laurent