From d03f9c320234d2e49ad8b2f4abd049a497afa2be Mon Sep 17 00:00:00 2001 From: Meador Inge Date: Mon, 6 Jul 2015 11:03:38 -0700 Subject: [PATCH 01/13] linux-user: Exit 0 when -h is used Signed-off-by: Meador Inge Signed-off-by: Riku Voipio --- linux-user/main.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 25cf8755ee..75932127a7 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -63,7 +63,7 @@ unsigned long reserved_va = 0xf7000000; unsigned long reserved_va; #endif -static void usage(void); +static void usage(int exitcode); static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; const char *qemu_uname_release; @@ -3700,7 +3700,7 @@ CPUArchState *cpu_copy(CPUArchState *env) static void handle_arg_help(const char *arg) { - usage(); + usage(0); } static void handle_arg_log(const char *arg) @@ -3726,7 +3726,7 @@ static void handle_arg_set_env(const char *arg) r = p = strdup(arg); while ((token = strsep(&p, ",")) != NULL) { if (envlist_setenv(envlist, token) != 0) { - usage(); + usage(1); } } free(r); @@ -3738,7 +3738,7 @@ static void handle_arg_unset_env(const char *arg) r = p = strdup(arg); while ((token = strsep(&p, ",")) != NULL) { if (envlist_unsetenv(envlist, token) != 0) { - usage(); + usage(1); } } free(r); @@ -3754,7 +3754,7 @@ static void handle_arg_stack_size(const char *arg) char *p; guest_stack_size = strtoul(arg, &p, 0); if (guest_stack_size == 0) { - usage(); + usage(1); } if (*p == 'M') { @@ -3921,7 +3921,7 @@ static const struct qemu_argument arg_table[] = { {NULL, NULL, false, NULL, NULL, NULL} }; -static void usage(void) +static void usage(int exitcode) { const struct qemu_argument *arginfo; int maxarglen; @@ -3988,7 +3988,7 @@ static void usage(void) "Note that if you provide several changes to a single variable\n" "the last change will stay in effect.\n"); - exit(1); + exit(exitcode); } static int parse_args(int argc, char **argv) @@ -4027,7 +4027,7 @@ static int parse_args(int argc, char **argv) if (!strcmp(r, arginfo->argv)) { if (arginfo->has_arg) { if (optind >= argc) { - usage(); + usage(1); } arginfo->handle_opt(argv[optind]); optind++; @@ -4040,12 +4040,12 @@ static int parse_args(int argc, char **argv) /* no option matched the current argv */ if (arginfo->handle_opt == NULL) { - usage(); + usage(1); } } if (optind >= argc) { - usage(); + usage(1); } filename = argv[optind]; From daaf8c8eb7135386134bc7075b9cf4dd57107c43 Mon Sep 17 00:00:00 2001 From: Meador Inge Date: Mon, 6 Jul 2015 11:03:39 -0700 Subject: [PATCH 02/13] linux-user: Add -help This option is already available on the system mode binaries. It would be better if long options were supported (i.e. --help), but this is okay for now. Signed-off-by: Meador Inge Signed-off-by: Riku Voipio --- linux-user/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/linux-user/main.c b/linux-user/main.c index 75932127a7..58d8d8dff5 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3883,6 +3883,8 @@ struct qemu_argument { static const struct qemu_argument arg_table[] = { {"h", "", false, handle_arg_help, "", "print this help"}, + {"help", "", false, handle_arg_help, + "", ""}, {"g", "QEMU_GDB", true, handle_arg_gdb, "port", "wait gdb connection to 'port'"}, {"L", "QEMU_LD_PREFIX", true, handle_arg_ld_prefix, From 138940bf08df1d8e9b862ad019a0d7d451e2413a Mon Sep 17 00:00:00 2001 From: Meador Inge Date: Mon, 6 Jul 2015 11:03:40 -0700 Subject: [PATCH 03/13] linux-user: Add proper error messages for bad options This patch adds better support for diagnosing option parser errors. The previous implementation just printed the usage text and exited when a bad option or argument was found. This made it very difficult to determine why the usage was being displayed and it was doubly confusing for cases like '--help' (it wasn't clear that --help was actually an error). Signed-off-by: Meador Inge Signed-off-by: Riku Voipio --- linux-user/main.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 58d8d8dff5..31aa4d98e4 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -4029,7 +4029,9 @@ static int parse_args(int argc, char **argv) if (!strcmp(r, arginfo->argv)) { if (arginfo->has_arg) { if (optind >= argc) { - usage(1); + (void) fprintf(stderr, + "qemu: missing argument for option '%s'\n", r); + exit(1); } arginfo->handle_opt(argv[optind]); optind++; @@ -4042,12 +4044,14 @@ static int parse_args(int argc, char **argv) /* no option matched the current argv */ if (arginfo->handle_opt == NULL) { - usage(1); + (void) fprintf(stderr, "qemu: unknown option '%s'\n", r); + exit(1); } } if (optind >= argc) { - usage(1); + (void) fprintf(stderr, "qemu: no user program specified\n"); + exit(1); } filename = argv[optind]; From 4d1275c24d5d64d22ec4a30ce1b6a0db3ba9a25a Mon Sep 17 00:00:00 2001 From: Riku Voipio Date: Mon, 28 Sep 2015 16:12:16 +0300 Subject: [PATCH 04/13] linux-user: use EXIT_SUCCESS and EXIT_FAILURE As suggested by Laurent, use EXIT_SUCCESS and EXIT_FAILURE from stdlib.h instead of numeric values. Cc: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/main.c | 72 +++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index 31aa4d98e4..c7c39d4d5c 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1414,7 +1414,7 @@ void cpu_loop (CPUSPARCState *env) default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); - exit (1); + exit(EXIT_FAILURE); } process_pending_signals (env); } @@ -2662,7 +2662,7 @@ void cpu_loop(CPUOpenRISCState *env) switch (trapnr) { case EXCP_RESET: qemu_log("\nReset request, exit, pc is %#x\n", env->pc); - exit(1); + exit(EXIT_FAILURE); break; case EXCP_BUSERR: qemu_log("\nBus error, exit, pc is %#x\n", env->pc); @@ -2726,7 +2726,7 @@ void cpu_loop(CPUOpenRISCState *env) if (gdbsig) { gdb_handlesig(cs, gdbsig); if (gdbsig != TARGET_SIGTRAP) { - exit(1); + exit(EXIT_FAILURE); } } @@ -2791,7 +2791,7 @@ void cpu_loop(CPUSH4State *env) default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); - exit (1); + exit(EXIT_FAILURE); } process_pending_signals (env); } @@ -2852,7 +2852,7 @@ void cpu_loop(CPUCRISState *env) default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); - exit (1); + exit(EXIT_FAILURE); } process_pending_signals (env); } @@ -2933,7 +2933,7 @@ void cpu_loop(CPUMBState *env) printf ("Unhandled hw-exception: 0x%x\n", env->sregs[SR_ESR] & ESR_EC_MASK); cpu_dump_state(cs, stderr, fprintf, 0); - exit (1); + exit(EXIT_FAILURE); break; } break; @@ -2954,7 +2954,7 @@ void cpu_loop(CPUMBState *env) default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); - exit (1); + exit(EXIT_FAILURE); } process_pending_signals (env); } @@ -3123,17 +3123,17 @@ void cpu_loop(CPUAlphaState *env) switch (trapnr) { case EXCP_RESET: fprintf(stderr, "Reset requested. Exit\n"); - exit(1); + exit(EXIT_FAILURE); break; case EXCP_MCHK: fprintf(stderr, "Machine check exception. Exit\n"); - exit(1); + exit(EXIT_FAILURE); break; case EXCP_SMP_INTERRUPT: case EXCP_CLK_INTERRUPT: case EXCP_DEV_INTERRUPT: fprintf(stderr, "External interrupt. Exit\n"); - exit(1); + exit(EXIT_FAILURE); break; case EXCP_MMFAULT: env->lock_addr = -1; @@ -3283,7 +3283,7 @@ void cpu_loop(CPUAlphaState *env) default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); - exit (1); + exit(EXIT_FAILURE); } process_pending_signals (env); } @@ -3387,7 +3387,7 @@ void cpu_loop(CPUS390XState *env) default: fprintf(stderr, "Unhandled program exception: %#x\n", n); cpu_dump_state(cs, stderr, fprintf, 0); - exit(1); + exit(EXIT_FAILURE); } break; @@ -3404,7 +3404,7 @@ void cpu_loop(CPUS390XState *env) default: fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(cs, stderr, fprintf, 0); - exit(1); + exit(EXIT_FAILURE); } process_pending_signals (env); } @@ -3700,7 +3700,7 @@ CPUArchState *cpu_copy(CPUArchState *env) static void handle_arg_help(const char *arg) { - usage(0); + usage(EXIT_SUCCESS); } static void handle_arg_log(const char *arg) @@ -3710,7 +3710,7 @@ static void handle_arg_log(const char *arg) mask = qemu_str_to_log_mask(arg); if (!mask) { qemu_print_log_usage(stdout); - exit(1); + exit(EXIT_FAILURE); } qemu_set_log(mask); } @@ -3726,7 +3726,7 @@ static void handle_arg_set_env(const char *arg) r = p = strdup(arg); while ((token = strsep(&p, ",")) != NULL) { if (envlist_setenv(envlist, token) != 0) { - usage(1); + usage(EXIT_FAILURE); } } free(r); @@ -3738,7 +3738,7 @@ static void handle_arg_unset_env(const char *arg) r = p = strdup(arg); while ((token = strsep(&p, ",")) != NULL) { if (envlist_unsetenv(envlist, token) != 0) { - usage(1); + usage(EXIT_FAILURE); } } free(r); @@ -3754,7 +3754,7 @@ static void handle_arg_stack_size(const char *arg) char *p; guest_stack_size = strtoul(arg, &p, 0); if (guest_stack_size == 0) { - usage(1); + usage(EXIT_FAILURE); } if (*p == 'M') { @@ -3775,7 +3775,7 @@ static void handle_arg_pagesize(const char *arg) if (qemu_host_page_size == 0 || (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) { fprintf(stderr, "page size must be a power of two\n"); - exit(1); + exit(EXIT_FAILURE); } } @@ -3785,7 +3785,7 @@ static void handle_arg_randseed(const char *arg) if (parse_uint_full(arg, &seed, 0) != 0 || seed > UINT_MAX) { fprintf(stderr, "Invalid seed number: %s\n", arg); - exit(1); + exit(EXIT_FAILURE); } srand(seed); } @@ -3808,7 +3808,7 @@ static void handle_arg_cpu(const char *arg) #if defined(cpu_list) cpu_list(stdout, &fprintf); #endif - exit(1); + exit(EXIT_FAILURE); } } @@ -3845,12 +3845,12 @@ static void handle_arg_reserved_va(const char *arg) #endif ) { fprintf(stderr, "Reserved virtual address too big\n"); - exit(1); + exit(EXIT_FAILURE); } } if (*p) { fprintf(stderr, "Unrecognised -R size suffix '%s'\n", p); - exit(1); + exit(EXIT_FAILURE); } } @@ -3868,7 +3868,7 @@ static void handle_arg_version(const char *arg) { printf("qemu-" TARGET_NAME " version " QEMU_VERSION QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"); - exit(0); + exit(EXIT_SUCCESS); } struct qemu_argument { @@ -4031,7 +4031,7 @@ static int parse_args(int argc, char **argv) if (optind >= argc) { (void) fprintf(stderr, "qemu: missing argument for option '%s'\n", r); - exit(1); + exit(EXIT_FAILURE); } arginfo->handle_opt(argv[optind]); optind++; @@ -4045,13 +4045,13 @@ static int parse_args(int argc, char **argv) /* no option matched the current argv */ if (arginfo->handle_opt == NULL) { (void) fprintf(stderr, "qemu: unknown option '%s'\n", r); - exit(1); + exit(EXIT_FAILURE); } } if (optind >= argc) { (void) fprintf(stderr, "qemu: no user program specified\n"); - exit(1); + exit(EXIT_FAILURE); } filename = argv[optind]; @@ -4080,7 +4080,7 @@ int main(int argc, char **argv, char **envp) if ((envlist = envlist_create()) == NULL) { (void) fprintf(stderr, "Unable to allocate envlist\n"); - exit(1); + exit(EXIT_FAILURE); } /* add current environment into the list */ @@ -4166,7 +4166,7 @@ int main(int argc, char **argv, char **envp) cpu = cpu_init(cpu_model); if (!cpu) { fprintf(stderr, "Unable to find CPU definition\n"); - exit(1); + exit(EXIT_FAILURE); } env = cpu->env_ptr; cpu_reset(cpu); @@ -4198,7 +4198,7 @@ int main(int argc, char **argv, char **envp) "space for use as guest address space (check your virtual " "memory ulimit setting or reserve less using -R option)\n", reserved_va); - exit(1); + exit(EXIT_FAILURE); } if (reserved_va) { @@ -4231,7 +4231,7 @@ int main(int argc, char **argv, char **envp) target_argv = calloc(target_argc + 1, sizeof (char *)); if (target_argv == NULL) { (void) fprintf(stderr, "Unable to allocate memory for target_argv\n"); - exit(1); + exit(EXIT_FAILURE); } /* @@ -4260,7 +4260,7 @@ int main(int argc, char **argv, char **envp) execfd = open(filename, O_RDONLY); if (execfd < 0) { printf("Error while loading %s: %s\n", filename, strerror(errno)); - _exit(1); + _exit(EXIT_FAILURE); } } @@ -4268,7 +4268,7 @@ int main(int argc, char **argv, char **envp) info, &bprm); if (ret != 0) { printf("Error while loading %s: %s\n", filename, strerror(-ret)); - _exit(1); + _exit(EXIT_FAILURE); } for (wrk = target_environ; *wrk; wrk++) { @@ -4314,7 +4314,7 @@ int main(int argc, char **argv, char **envp) /* enable 64 bit mode if possible */ if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) { fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n"); - exit(1); + exit(EXIT_FAILURE); } env->cr[4] |= CR4_PAE_MASK; env->efer |= MSR_EFER_LMA | MSR_EFER_LME; @@ -4424,7 +4424,7 @@ int main(int argc, char **argv, char **envp) if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { fprintf(stderr, "The selected ARM CPU does not support 64 bit mode\n"); - exit(1); + exit(EXIT_FAILURE); } for (i = 0; i < 31; i++) { @@ -4636,7 +4636,7 @@ int main(int argc, char **argv, char **envp) if (gdbserver_start(gdbstub_port) < 0) { fprintf(stderr, "qemu: could not open gdbserver on port %d\n", gdbstub_port); - exit(1); + exit(EXIT_FAILURE); } gdb_handlesig(cpu, 0); } From ba02577cadbe5b53db791522310c977f9960f81f Mon Sep 17 00:00:00 2001 From: Meador Inge Date: Mon, 6 Jul 2015 11:03:41 -0700 Subject: [PATCH 05/13] linux-user: Treat --foo options the same as -foo The system mode binaries provide a similar alias and it makes common options like --version and --help work as expected. Signed-off-by: Meador Inge Signed-off-by: Riku Voipio --- linux-user/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/linux-user/main.c b/linux-user/main.c index c7c39d4d5c..6599a41404 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -4024,6 +4024,10 @@ static int parse_args(int argc, char **argv) if (!strcmp(r, "-")) { break; } + /* Treat --foo the same as -foo. */ + if (r[0] == '-') { + r++; + } for (arginfo = arg_table; arginfo->handle_opt != NULL; arginfo++) { if (!strcmp(r, arginfo->argv)) { From 84646ee25b68321624ef4768011e91064e4bd440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Br=C3=BCns?= Date: Wed, 2 Sep 2015 03:38:52 +0200 Subject: [PATCH 06/13] linux-user: remove unused image_info members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Peter Maydell Signed-off-by: Stefan Brüns Signed-off-by: Riku Voipio --- linux-user/elfload.c | 3 --- linux-user/qemu.h | 2 -- 2 files changed, 5 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a7ff58c8bb..ad46530f83 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1455,7 +1455,6 @@ static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm, for (i = 0 ; i < MAX_ARG_PAGES ; i++) { if (bprm->page[i]) { - info->rss++; /* FIXME - check return value of memcpy_to_target() for failure */ memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE); g_free(bprm->page[i]); @@ -2206,8 +2205,6 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) char *elf_interpreter = NULL; info->start_mmap = (abi_ulong)ELF_START_MMAP; - info->mmap = 0; - info->rss = 0; load_elf_image(bprm->filename, bprm->fd, info, &elf_interpreter, bprm->buf); diff --git a/linux-user/qemu.h b/linux-user/qemu.h index e8606b290c..678e5d602e 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -36,8 +36,6 @@ struct image_info { abi_ulong start_brk; abi_ulong brk; abi_ulong start_mmap; - abi_ulong mmap; - abi_ulong rss; abi_ulong start_stack; abi_ulong stack_limit; abi_ulong entry; From 59baae9a626396a3a05840279084c4bf2beb8f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Br=C3=BCns?= Date: Wed, 2 Sep 2015 03:38:53 +0200 Subject: [PATCH 07/13] linux-user: remove MAX_ARG_PAGES limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of creating a temporary copy for the whole environment and the arguments, directly copy everything to the target stack. For this to work, we have to change the order of stack creation and copying the arguments. Reviewed-by: Peter Maydell Signed-off-by: Stefan Brüns Signed-off-by: Riku Voipio --- linux-user/elfload.c | 108 ++++++++++++++++++++--------------------- linux-user/flatload.c | 2 +- linux-user/linuxload.c | 7 --- linux-user/qemu.h | 7 --- linux-user/syscall.c | 6 --- 5 files changed, 55 insertions(+), 75 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ad46530f83..fdae6a6cd1 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1373,66 +1373,69 @@ static bool elf_check_ehdr(struct elfhdr *ehdr) * to be put directly into the top of new user memory. * */ -static abi_ulong copy_elf_strings(int argc,char ** argv, void **page, - abi_ulong p) +static abi_ulong copy_elf_strings(int argc, char **argv, char *scratch, + abi_ulong p, abi_ulong stack_limit) { - char *tmp, *tmp1, *pag = NULL; - int len, offset = 0; + char *tmp; + int len, offset; + abi_ulong top = p; if (!p) { return 0; /* bullet-proofing */ } + + offset = ((p - 1) % TARGET_PAGE_SIZE) + 1; + while (argc-- > 0) { tmp = argv[argc]; if (!tmp) { fprintf(stderr, "VFS: argc is wrong"); exit(-1); } - tmp1 = tmp; - while (*tmp++); - len = tmp - tmp1; - if (p < len) { /* this shouldn't happen - 128kB */ + len = strlen(tmp) + 1; + tmp += len; + + if (len > (p - stack_limit)) { return 0; } while (len) { - --p; --tmp; --len; - if (--offset < 0) { - offset = p % TARGET_PAGE_SIZE; - pag = (char *)page[p/TARGET_PAGE_SIZE]; - if (!pag) { - pag = g_try_malloc0(TARGET_PAGE_SIZE); - page[p/TARGET_PAGE_SIZE] = pag; - if (!pag) - return 0; - } - } - if (len == 0 || offset == 0) { - *(pag + offset) = *tmp; - } - else { - int bytes_to_copy = (len > offset) ? offset : len; - tmp -= bytes_to_copy; - p -= bytes_to_copy; - offset -= bytes_to_copy; - len -= bytes_to_copy; - memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1); + int bytes_to_copy = (len > offset) ? offset : len; + tmp -= bytes_to_copy; + p -= bytes_to_copy; + offset -= bytes_to_copy; + len -= bytes_to_copy; + + memcpy_fromfs(scratch + offset, tmp, bytes_to_copy); + + if (offset == 0) { + memcpy_to_target(p, scratch, top - p); + top = p; + offset = TARGET_PAGE_SIZE; } } } + if (offset) { + memcpy_to_target(p, scratch + offset, top - p); + } + return p; } -static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm, +/* Older linux kernels provide up to MAX_ARG_PAGES (default: 32) of + * argument/environment space. Newer kernels (>2.6.33) allow more, + * dependent on stack size, but guarantee at least 32 pages for + * backwards compatibility. + */ +#define STACK_LOWER_LIMIT (32 * TARGET_PAGE_SIZE) + +static abi_ulong setup_arg_pages(struct linux_binprm *bprm, struct image_info *info) { - abi_ulong stack_base, size, error, guard; - int i; + abi_ulong size, error, guard; - /* Create enough stack to hold everything. If we don't use - it for args, we'll use it for something else. */ size = guest_stack_size; - if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE) { - size = MAX_ARG_PAGES*TARGET_PAGE_SIZE; + if (size < STACK_LOWER_LIMIT) { + size = STACK_LOWER_LIMIT; } guard = TARGET_PAGE_SIZE; if (guard < qemu_real_host_page_size) { @@ -1450,18 +1453,8 @@ static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm, target_mprotect(error, guard, PROT_NONE); info->stack_limit = error + guard; - stack_base = info->stack_limit + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE; - p += stack_base; - for (i = 0 ; i < MAX_ARG_PAGES ; i++) { - if (bprm->page[i]) { - /* FIXME - check return value of memcpy_to_target() for failure */ - memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE); - g_free(bprm->page[i]); - } - stack_base += TARGET_PAGE_SIZE; - } - return p; + return info->stack_limit + size - sizeof(void *); } /* Map and zero the bss. We need to explicitly zero any fractional pages @@ -2203,6 +2196,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) struct image_info interp_info; struct elfhdr elf_ex; char *elf_interpreter = NULL; + char *scratch; info->start_mmap = (abi_ulong)ELF_START_MMAP; @@ -2214,18 +2208,24 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) when we load the interpreter. */ elf_ex = *(struct elfhdr *)bprm->buf; - bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p); - bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p); - bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p); + /* Do this so that we can load the interpreter, if need be. We will + change some of these later */ + bprm->p = setup_arg_pages(bprm, info); + + scratch = g_new0(char, TARGET_PAGE_SIZE); + bprm->p = copy_elf_strings(1, &bprm->filename, scratch, + bprm->p, info->stack_limit); + bprm->p = copy_elf_strings(bprm->envc, bprm->envp, scratch, + bprm->p, info->stack_limit); + bprm->p = copy_elf_strings(bprm->argc, bprm->argv, scratch, + bprm->p, info->stack_limit); + g_free(scratch); + if (!bprm->p) { fprintf(stderr, "%s: %s\n", bprm->filename, strerror(E2BIG)); exit(-1); } - /* Do this so that we can load the interpreter, if need be. We will - change some of these later */ - bprm->p = setup_arg_pages(bprm->p, bprm, info); - if (elf_interpreter) { load_elf_interp(elf_interpreter, &interp_info, bprm->buf); diff --git a/linux-user/flatload.c b/linux-user/flatload.c index 566a7a87a3..ceacb9844a 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -707,7 +707,7 @@ static int load_flat_shared_library(int id, struct lib_info *libs) int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) { struct lib_info libinfo[MAX_SHARED_LIBS]; - abi_ulong p = bprm->p; + abi_ulong p; abi_ulong stack_len; abi_ulong start_addr; abi_ulong sp; diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index 506e837ae1..dbaf0ec586 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -135,10 +135,7 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, struct linux_binprm *bprm) { int retval; - int i; - bprm->p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int); - memset(bprm->page, 0, sizeof(bprm->page)); bprm->fd = fdexec; bprm->filename = (char *)filename; bprm->argc = count(argv); @@ -172,9 +169,5 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, return retval; } - /* Something went wrong, return the inode and free the argument pages*/ - for (i=0 ; ipage[i]); - } return(retval); } diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 678e5d602e..bd90cc3799 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -143,12 +143,6 @@ extern const char *qemu_uname_release; extern unsigned long mmap_min_addr; /* ??? See if we can avoid exposing so much of the loader internals. */ -/* - * MAX_ARG_PAGES defines the number of pages allocated for arguments - * and envelope for the new program. 32 should suffice, this gives - * a maximum env+arg of 128kB w/4KB pages! - */ -#define MAX_ARG_PAGES 33 /* Read a good amount of data initially, to hopefully get all the program headers loaded. */ @@ -160,7 +154,6 @@ extern unsigned long mmap_min_addr; */ struct linux_binprm { char buf[BPRM_BUF_SIZE] __attribute__((aligned)); - void *page[MAX_ARG_PAGES]; abi_ulong p; int fd; int e_uid, e_gid; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index d1d3eb2d78..7fbaa8bb67 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5808,12 +5808,6 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } *q = NULL; - /* This case will not be caught by the host's execve() if its - page size is bigger than the target's. */ - if (total_size > MAX_ARG_PAGES * TARGET_PAGE_SIZE) { - ret = -TARGET_E2BIG; - goto execve_end; - } if (!(p = lock_user_string(arg1))) goto execve_efault; ret = get_errno(execve(p, argp, envp)); From ee1045877a7e226945c7cec2bda80039bd2d0c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Neusch=C3=A4fer?= Date: Thu, 3 Sep 2015 07:27:26 +0200 Subject: [PATCH 08/13] linux-user: fix cmsg conversion in case of multiple headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, __target_cmsg_nxthdr compares a pointer derived from target_cmsg against the msg_control field of target_msgh (through subtraction). This failed for me when emulating i386 code under x86_64, because pointers in the host address space and pointers in the guest address space were not the same. This patch passes the initial value of target_cmsg into __target_cmsg_nxthdr. I found and fixed two more related bugs: - __target_cmsg_nxthdr now returns the new cmsg pointer instead of the old one. - tgt_space (in host_to_target_cmsg) doesn't count "sizeof (struct target_cmsghdr)" twice anymore. Reviewed-by: Peter Maydell Signed-off-by: Jonathan Neuschäfer Signed-off-by: Riku Voipio --- linux-user/syscall.c | 14 +++++++++----- linux-user/syscall_defs.h | 14 +++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 7fbaa8bb67..75ac32c05d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1181,7 +1181,7 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh); abi_long msg_controllen; abi_ulong target_cmsg_addr; - struct target_cmsghdr *target_cmsg; + struct target_cmsghdr *target_cmsg, *target_cmsg_start; socklen_t space = 0; msg_controllen = tswapal(target_msgh->msg_controllen); @@ -1189,6 +1189,7 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, goto the_end; target_cmsg_addr = tswapal(target_msgh->msg_control); target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1); + target_cmsg_start = target_cmsg; if (!target_cmsg) return -TARGET_EFAULT; @@ -1247,7 +1248,8 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, } cmsg = CMSG_NXTHDR(msgh, cmsg); - target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg); + target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg, + target_cmsg_start); } unlock_user(target_cmsg, target_cmsg_addr, 0); the_end: @@ -1261,7 +1263,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, struct cmsghdr *cmsg = CMSG_FIRSTHDR(msgh); abi_long msg_controllen; abi_ulong target_cmsg_addr; - struct target_cmsghdr *target_cmsg; + struct target_cmsghdr *target_cmsg, *target_cmsg_start; socklen_t space = 0; msg_controllen = tswapal(target_msgh->msg_controllen); @@ -1269,6 +1271,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, goto the_end; target_cmsg_addr = tswapal(target_msgh->msg_control); target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0); + target_cmsg_start = target_cmsg; if (!target_cmsg) return -TARGET_EFAULT; @@ -1382,14 +1385,15 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, } target_cmsg->cmsg_len = tswapal(tgt_len); - tgt_space = TARGET_CMSG_SPACE(tgt_len); + tgt_space = TARGET_CMSG_SPACE(len); if (msg_controllen < tgt_space) { tgt_space = msg_controllen; } msg_controllen -= tgt_space; space += tgt_space; cmsg = CMSG_NXTHDR(msgh, cmsg); - target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg); + target_cmsg = TARGET_CMSG_NXTHDR(target_msgh, target_cmsg, + target_cmsg_start); } unlock_user(target_cmsg, target_cmsg_addr, space); the_end: diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index cdc8db421c..7ca33a6f62 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -235,7 +235,8 @@ struct target_cmsghdr { }; #define TARGET_CMSG_DATA(cmsg) ((unsigned char *) ((struct target_cmsghdr *) (cmsg) + 1)) -#define TARGET_CMSG_NXTHDR(mhdr, cmsg) __target_cmsg_nxthdr (mhdr, cmsg) +#define TARGET_CMSG_NXTHDR(mhdr, cmsg, cmsg_start) \ + __target_cmsg_nxthdr(mhdr, cmsg, cmsg_start) #define TARGET_CMSG_ALIGN(len) (((len) + sizeof (abi_long) - 1) \ & (size_t) ~(sizeof (abi_long) - 1)) #define TARGET_CMSG_SPACE(len) (TARGET_CMSG_ALIGN (len) \ @@ -243,17 +244,20 @@ struct target_cmsghdr { #define TARGET_CMSG_LEN(len) (TARGET_CMSG_ALIGN (sizeof (struct target_cmsghdr)) + (len)) static __inline__ struct target_cmsghdr * -__target_cmsg_nxthdr (struct target_msghdr *__mhdr, struct target_cmsghdr *__cmsg) +__target_cmsg_nxthdr(struct target_msghdr *__mhdr, + struct target_cmsghdr *__cmsg, + struct target_cmsghdr *__cmsg_start) { struct target_cmsghdr *__ptr; __ptr = (struct target_cmsghdr *)((unsigned char *) __cmsg + TARGET_CMSG_ALIGN (tswapal(__cmsg->cmsg_len))); - if ((unsigned long)((char *)(__ptr+1) - (char *)(size_t)tswapal(__mhdr->msg_control)) - > tswapal(__mhdr->msg_controllen)) + if ((unsigned long)((char *)(__ptr+1) - (char *)__cmsg_start) + > tswapal(__mhdr->msg_controllen)) { /* No more entries. */ return (struct target_cmsghdr *)0; - return __cmsg; + } + return __ptr; } struct target_mmsghdr { From 93b4eff80af9822e4b726dcf21ee61538e088695 Mon Sep 17 00:00:00 2001 From: Timothy E Baldwin Date: Mon, 31 Aug 2015 00:26:21 +0100 Subject: [PATCH 09/13] linux-user: Return target error number in do_fork() Whilst calls to do_fork() are wrapped in get_errno() this does not translate return values. Reviewed-by: Peter Maydell Signed-off-by: Timothy Edward Baldwin Signed-off-by: Riku Voipio --- linux-user/syscall.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 75ac32c05d..c25ffd8878 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -4626,8 +4626,9 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, pthread_mutex_unlock(&clone_lock); } else { /* if no CLONE_VM, we consider it is a fork */ - if ((flags & ~(CSIGNAL | CLONE_NPTL_FLAGS2)) != 0) - return -EINVAL; + if ((flags & ~(CSIGNAL | CLONE_NPTL_FLAGS2)) != 0) { + return -TARGET_EINVAL; + } fork_start(); ret = fork(); if (ret == 0) { From 0f0426f343886fb5c9f137c2830f35cc2dae7327 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Tue, 1 Sep 2015 22:27:33 +0200 Subject: [PATCH 10/13] linux-user: add name_to_handle_at/open_by_handle_at This patch allows to run example given by open_by_handle_at(2): The following shell session demonstrates the use of these two programs: $ echo 'Can you please think about it?' > cecilia.txt $ ./t_name_to_handle_at cecilia.txt > fh $ ./t_open_by_handle_at < fh open_by_handle_at: Operation not permitted $ sudo ./t_open_by_handle_at < fh # Need CAP_SYS_ADMIN Read 31 bytes $ rm cecilia.txt Now we delete and (quickly) re-create the file so that it has the same content and (by chance) the same inode.[...] $ stat --printf="%i\n" cecilia.txt # Display inode number 4072121 $ rm cecilia.txt $ echo 'Can you please think about it?' > cecilia.txt $ stat --printf="%i\n" cecilia.txt # Check inode number 4072121 $ sudo ./t_open_by_handle_at < fh open_by_handle_at: Stale NFS file handle See the man page for source code. Reviewed-by: Peter Maydell Signed-off-by: Laurent Vivier Signed-off-by: Riku Voipio --- linux-user/syscall.c | 98 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index c25ffd8878..952834422d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5251,6 +5251,94 @@ static int do_futex(target_ulong uaddr, int op, int val, target_ulong timeout, return -TARGET_ENOSYS; } } +#if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) +static abi_long do_name_to_handle_at(abi_long dirfd, abi_long pathname, + abi_long handle, abi_long mount_id, + abi_long flags) +{ + struct file_handle *target_fh; + struct file_handle *fh; + int mid = 0; + abi_long ret; + char *name; + unsigned int size, total_size; + + if (get_user_s32(size, handle)) { + return -TARGET_EFAULT; + } + + name = lock_user_string(pathname); + if (!name) { + return -TARGET_EFAULT; + } + + total_size = sizeof(struct file_handle) + size; + target_fh = lock_user(VERIFY_WRITE, handle, total_size, 0); + if (!target_fh) { + unlock_user(name, pathname, 0); + return -TARGET_EFAULT; + } + + fh = g_malloc0(total_size); + fh->handle_bytes = size; + + ret = get_errno(name_to_handle_at(dirfd, path(name), fh, &mid, flags)); + unlock_user(name, pathname, 0); + + /* man name_to_handle_at(2): + * Other than the use of the handle_bytes field, the caller should treat + * the file_handle structure as an opaque data type + */ + + memcpy(target_fh, fh, total_size); + target_fh->handle_bytes = tswap32(fh->handle_bytes); + target_fh->handle_type = tswap32(fh->handle_type); + g_free(fh); + unlock_user(target_fh, handle, total_size); + + if (put_user_s32(mid, mount_id)) { + return -TARGET_EFAULT; + } + + return ret; + +} +#endif + +#if defined(TARGET_NR_open_by_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) +static abi_long do_open_by_handle_at(abi_long mount_fd, abi_long handle, + abi_long flags) +{ + struct file_handle *target_fh; + struct file_handle *fh; + unsigned int size, total_size; + abi_long ret; + + if (get_user_s32(size, handle)) { + return -TARGET_EFAULT; + } + + total_size = sizeof(struct file_handle) + size; + target_fh = lock_user(VERIFY_READ, handle, total_size, 1); + if (!target_fh) { + return -TARGET_EFAULT; + } + + fh = g_malloc0(total_size); + memcpy(fh, target_fh, total_size); + fh->handle_bytes = size; + fh->handle_type = tswap32(target_fh->handle_type); + + ret = get_errno(open_by_handle_at(mount_fd, fh, + target_to_host_bitmask(flags, fcntl_flags_tbl))); + + g_free(fh); + + unlock_user(target_fh, handle, total_size); + + return ret; +} +#endif /* Map host to target signal numbers for the wait family of syscalls. Assume all other status bits are the same. */ @@ -5663,6 +5751,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, arg4)); unlock_user(p, arg2, 0); break; +#if defined(TARGET_NR_name_to_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) + case TARGET_NR_name_to_handle_at: + ret = do_name_to_handle_at(arg1, arg2, arg3, arg4, arg5); + break; +#endif +#if defined(TARGET_NR_open_by_handle_at) && defined(CONFIG_OPEN_BY_HANDLE) + case TARGET_NR_open_by_handle_at: + ret = do_open_by_handle_at(arg1, arg2, arg3); + break; +#endif case TARGET_NR_close: ret = get_errno(close(arg1)); break; From 08703b9f7bcd7ca2152d51a4c8893d26f1dc28de Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Mon, 7 Sep 2015 10:35:06 +0800 Subject: [PATCH 11/13] linux-user/syscall.c: Add EAGAIN to host_to_target_errno_table for Under Alpha host, EAGAIN is redefined to 35, so it need be remapped too. Reviewed-by: Peter Maydell Signed-off-by: Chen Gang Signed-off-by: Riku Voipio --- linux-user/syscall.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 952834422d..98b5766d4a 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -457,6 +457,7 @@ static uint16_t target_to_host_errno_table[ERRNO_TABLE_SIZE] = { * minus the errnos that are not actually generic to all archs. */ static uint16_t host_to_target_errno_table[ERRNO_TABLE_SIZE] = { + [EAGAIN] = TARGET_EAGAIN, [EIDRM] = TARGET_EIDRM, [ECHRNG] = TARGET_ECHRNG, [EL2NSYNC] = TARGET_EL2NSYNC, From d0924a26d8f37ab95fdef99f6850b93e9af3ffb2 Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Sat, 12 Sep 2015 23:32:30 +0800 Subject: [PATCH 12/13] linux-user/signal.c: Use setup_rt_frame() instead of setup_frame() for target openrisc qemu has already considered about some targets may have no traditional signals. And openrisc's setup_frame() is dummy, but it can be supported by setup_rt_frame(). Reviewed-by: Peter Maydell Signed-off-by: Chen Gang Signed-off-by: Riku Voipio --- linux-user/signal.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/linux-user/signal.c b/linux-user/signal.c index 502efd9fc4..ac82baa0f0 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3900,12 +3900,6 @@ static inline abi_ulong get_sigframe(struct target_sigaction *ka, return sp; } -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUOpenRISCState *env) -{ - qemu_log("Not implement.\n"); -} - static void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUOpenRISCState *env) @@ -5662,7 +5656,8 @@ void process_pending_signals(CPUArchState *cpu_env) } #endif /* prepare the stack frame of the virtual CPU */ -#if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64) +#if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64) \ + || defined(TARGET_OPENRISC) /* These targets do not have traditional signals. */ setup_rt_frame(sig, sa, &q->info, &target_old_set, cpu_env); #else From 86abac06c142d20772b3f2e04c9bf02b7936a0b3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 14 Sep 2015 12:31:44 +0200 Subject: [PATCH 13/13] linux-user: assert that target_mprotect cannot fail All error conditions that target_mprotect checks are also checked by target_mmap. EACCESS cannot happen because we are just removing PROT_WRITE. ENOMEM should not happen because we are modifying a whole VMA (and we have bigger problems anyway if it happens). Fixes a Coverity false positive, where Coverity complains about target_mprotect's return value being passed to tb_invalidate_phys_range. Signed-off-by: Paolo Bonzini Signed-off-by: Riku Voipio --- linux-user/mmap.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/linux-user/mmap.c b/linux-user/mmap.c index b2126c76fa..5606bcd164 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -514,10 +514,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, goto fail; if (!(prot & PROT_WRITE)) { ret = target_mprotect(start, len, prot); - if (ret != 0) { - start = ret; - goto the_end; - } + assert(ret == 0); } goto the_end; }