diff --git a/gdbstub.c b/gdbstub.c
index 8abcb8a..33805fe 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -317,8 +317,6 @@ static GDBState *gdbserver_state;
 
 bool gdb_has_xml;
 
-int semihosting_target = SEMIHOSTING_TARGET_AUTO;
-
 #ifdef CONFIG_USER_ONLY
 /* XXX: This is not thread safe.  Do we care?  */
 static int gdbserver_fd = -1;
@@ -356,10 +354,10 @@ static enum {
 /* Decide if either remote gdb syscalls or native file IO should be used. */
 int use_gdb_syscalls(void)
 {
-    if (semihosting_target == SEMIHOSTING_TARGET_NATIVE) {
+    if (semihosting.target == SEMIHOSTING_TARGET_NATIVE) {
         /* -semihosting-config target=native */
         return false;
-    } else if (semihosting_target == SEMIHOSTING_TARGET_GDB) {
+    } else if (semihosting.target == SEMIHOSTING_TARGET_GDB) {
         /* -semihosting-config target=gdb */
         return true;
     }
diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c
index c6eab6d..dc7fbef 100644
--- a/hw/arm/armv7m.c
+++ b/hw/arm/armv7m.c
@@ -179,6 +179,7 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
     int i;
     int big_endian;
     MemoryRegion *hack = g_new(MemoryRegion, 1);
+    static struct arm_boot_info boot_info;
 
     if (cpu_model == NULL) {
 	cpu_model = "cortex-m3";
@@ -213,6 +214,17 @@ qemu_irq *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
         exit(1);
     }
 
+    /*
+     * Fill-in a minimalistic boot info, required for semihosting.
+     * kernel_cmdline should be initialised with machine->kernel_cmdline,
+     * but we do not have machine here. Suggestion: change prototype
+     * and add "MachineState *machine".
+     */
+    boot_info.kernel_cmdline = "";
+    boot_info.kernel_filename = kernel_filename;
+
+    env->boot_info = &boot_info;
+
     if (kernel_filename) {
         image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
                               NULL, big_endian, ELF_MACHINE, 1);
diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index c633248..a608a26 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -95,10 +95,4 @@ extern bool gdb_has_xml;
 /* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */
 extern const char *const xml_builtin[][2];
 
-/* Command line option defining whether semihosting should go via gdb or not */
-extern int semihosting_target;
-#define SEMIHOSTING_TARGET_AUTO     0
-#define SEMIHOSTING_TARGET_NATIVE   1
-#define SEMIHOSTING_TARGET_GDB      2
-
 #endif
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
index 8a52934..932f2c3 100644
--- a/include/sysemu/sysemu.h
+++ b/include/sysemu/sysemu.h
@@ -125,7 +125,26 @@ extern int cursor_hide;
 extern int graphic_rotate;
 extern int no_quit;
 extern int no_shutdown;
-extern int semihosting_enabled;
+
+/* Define where the semihosting calls are sent. */
+#define SEMIHOSTING_TARGET_AUTO     0
+#define SEMIHOSTING_TARGET_NATIVE   1
+#define SEMIHOSTING_TARGET_GDB      2
+
+/* Semihosting status */
+typedef struct {
+    int enabled; /* 1 if enabled */
+    int target; /* see above */
+    int argc;
+    char **argv;
+    const char *cmdline; /* concatenated argv */
+} Semihosting;
+
+extern Semihosting semihosting;
+
+/* Temporary definition, until all references are replaced. */
+#define semihosting_enabled semihosting.enabled
+
 extern int old_param;
 extern int boot_menu;
 extern bool boot_strict;
diff --git a/qemu-options.hx b/qemu-options.hx
index 319d971..242d97a 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3305,6 +3305,19 @@ Enable semihosting and define where the semihosting calls will be addressed,
 to QEMU (@code{native}) or to GDB (@code{gdb}). The default is @code{auto}, which means
 @code{gdb} during debug sessions and @code{native} otherwise (ARM, M68K, Xtensa only).
 ETEXI
+DEF("semihosting-cmdline", 0, QEMU_OPTION_semihosting_cmdline,
+    "-semihosting-cmdline   semihosting command line\n",
+QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32)
+STEXI
+@item -semihosting-cmdline
+@findex -semihosting-cmdline
+The
+@code{cmdline} defines the entire command line passed to the application via the
+semihosting calls, including the program name that will be
+passed as argv[0]. Must be the last option, all following arguments
+are passed to the application unchanged.
+(ARM, M68K, Xtensa only)
+ETEXI
 DEF("old-param", 0, QEMU_OPTION_old_param,
     "-old-param      old param mode\n", QEMU_ARCH_ARM)
 STEXI
diff --git a/target-arm/arm-semi.c b/target-arm/arm-semi.c
index a8b83e6..6b302ad 100644
--- a/target-arm/arm-semi.c
+++ b/target-arm/arm-semi.c
@@ -35,6 +35,9 @@
 #include "qemu-common.h"
 #include "exec/gdbstub.h"
 #include "hw/arm/arm.h"
+#include "qemu/option.h"
+#include "qemu/config-file.h"
+#include "sysemu/sysemu.h"
 #endif
 
 #define TARGET_SYS_OPEN        0x01
@@ -440,10 +443,14 @@ uint32_t do_arm_semihosting(CPUARMState *env)
             input_size = arg1;
             /* Compute the size of the output string.  */
 #if !defined(CONFIG_USER_ONLY)
-            output_size = strlen(ts->boot_info->kernel_filename)
+            if (semihosting.cmdline) {
+                output_size = strlen(semihosting.cmdline) + 1;
+            } else {
+                output_size = strlen(ts->boot_info->kernel_filename)
                         + 1  /* Separating space.  */
                         + strlen(ts->boot_info->kernel_cmdline)
                         + 1; /* Terminating null byte.  */
+            }
 #else
             unsigned int i;
 
@@ -474,9 +481,15 @@ uint32_t do_arm_semihosting(CPUARMState *env)
 
             /* Copy the command-line arguments.  */
 #if !defined(CONFIG_USER_ONLY)
-            pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename);
-            pstrcat(output_buffer, output_size, " ");
-            pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
+            if (semihosting.cmdline) {
+                pstrcpy(output_buffer, output_size, semihosting.cmdline);
+            } else {
+                pstrcpy(output_buffer, output_size,
+                        ts->boot_info->kernel_filename);
+                pstrcat(output_buffer, output_size, " ");
+                pstrcat(output_buffer, output_size,
+                        ts->boot_info->kernel_cmdline);
+            }
 #else
             if (output_size == 1) {
                 /* Empty command-line.  */
diff --git a/vl.c b/vl.c
index 74c2681..3cd8cd5 100644
--- a/vl.c
+++ b/vl.c
@@ -170,7 +170,7 @@ int graphic_rotate = 0;
 const char *watchdog;
 QEMUOptionRom option_rom[MAX_OPTION_ROMS];
 int nb_option_roms;
-int semihosting_enabled = 0;
+Semihosting semihosting;
 int old_param = 0;
 const char *qemu_name;
 int alt_grab = 0;
@@ -2727,6 +2727,51 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size)
     }
 }
 
+static const char *concatenate_semihosting_cmdline(int argc, char **argv)
+{
+    int total_size = 0;
+    int i;
+
+    for (i = 0; i < argc; ++i) {
+        total_size += strlen(argv[i]);
+        total_size += 1; /* Add separator spaces */
+        if (rindex(argv[i], ' ') != NULL) {
+            total_size += 2; /* Provision for quotes */
+        }
+    }
+
+    char *cmdline = malloc(total_size);
+    char *p = cmdline;
+    *p = '\0'; /* Prepare for empty command line */
+    for (i = 0; i < argc; ++i) {
+        if (i != 0) {
+            *p++ = ' ';
+        }
+        if (rindex(argv[i], ' ') == NULL) {
+            strcpy(p, argv[i]);
+            p += strlen(argv[i]);
+        } else {
+            /* If no quotes found, it is safe to use them for grouping */
+            if (rindex(argv[i], '"') == NULL) {
+                *p++ = '"';
+                strcpy(p, argv[i]);
+                p += strlen(argv[i]);
+                *p++ = '"';
+            } else {
+                /* Does not work if string has both quotes and apostrophs */
+                *p++ = '\'';
+                strcpy(p, argv[i]);
+                p += strlen(argv[i]);
+                *p++ = '\'';
+            }
+        }
+        *p = '\0';
+    }
+
+    return cmdline;
+}
+
+
 int main(int argc, char **argv, char **envp)
 {
     int i;
@@ -2768,6 +2813,13 @@ int main(int argc, char **argv, char **envp)
     FILE *vmstate_dump_file = NULL;
     Error *main_loop_err = NULL;
 
+    int actual_argc = argc;
+
+    semihosting.enabled = 0;
+    semihosting.argc = 0;
+    semihosting.argv = NULL;
+    semihosting.cmdline = NULL;
+
     qemu_init_cpu_loop();
     qemu_mutex_lock_iothread();
 
@@ -2844,6 +2896,16 @@ int main(int argc, char **argv, char **envp)
             case QEMU_OPTION_nouserconfig:
                 userconfig = false;
                 break;
+            case QEMU_OPTION_semihosting_cmdline:
+                /* no HAS_ARGS, optind set to next option */
+                semihosting.argc = argc - optind;
+                semihosting.argv = &argv[optind];
+                /* Diminish count to hide semihosting command line */
+                actual_argc = optind - 1; /* exclude current option */
+
+                semihosting.cmdline = concatenate_semihosting_cmdline(
+                                        semihosting.argc, semihosting.argv);
+                break;
             }
         }
     }
@@ -2859,14 +2921,15 @@ int main(int argc, char **argv, char **envp)
     /* second pass of option parsing */
     optind = 1;
     for(;;) {
-        if (optind >= argc)
+        if (optind >= actual_argc) {
             break;
+        }
         if (argv[optind][0] != '-') {
             hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
         } else {
             const QEMUOption *popt;
 
-            popt = lookup_opt(argc, argv, &optarg, &optind);
+            popt = lookup_opt(actual_argc, argv, &optarg, &optind);
             if (!(popt->arch_mask & arch_type)) {
                 printf("Option %s not supported for this target\n", popt->name);
                 exit(1);
@@ -3535,24 +3598,24 @@ int main(int argc, char **argv, char **envp)
                 nb_option_roms++;
                 break;
             case QEMU_OPTION_semihosting:
-                semihosting_enabled = 1;
-                semihosting_target = SEMIHOSTING_TARGET_AUTO;
+                semihosting.enabled = 1;
+                semihosting.target = SEMIHOSTING_TARGET_AUTO;
                 break;
             case QEMU_OPTION_semihosting_config:
-                semihosting_enabled = 1;
+                semihosting.enabled = 1;
                 opts = qemu_opts_parse(qemu_find_opts("semihosting-config"),
                                            optarg, 0);
                 if (opts != NULL) {
-                    semihosting_enabled = qemu_opt_get_bool(opts, "enable",
+                    semihosting.enabled = qemu_opt_get_bool(opts, "enable",
                                                             true);
                     const char *target = qemu_opt_get(opts, "target");
                     if (target != NULL) {
                         if (strcmp("native", target) == 0) {
-                            semihosting_target = SEMIHOSTING_TARGET_NATIVE;
+                            semihosting.target = SEMIHOSTING_TARGET_NATIVE;
                         } else if (strcmp("gdb", target) == 0) {
-                            semihosting_target = SEMIHOSTING_TARGET_GDB;
+                            semihosting.target = SEMIHOSTING_TARGET_GDB;
                         } else  if (strcmp("auto", target) == 0) {
-                            semihosting_target = SEMIHOSTING_TARGET_AUTO;
+                            semihosting.target = SEMIHOSTING_TARGET_AUTO;
                         } else {
                             fprintf(stderr, "Unsupported semihosting-config"
                                     " %s\n",
@@ -3560,7 +3623,7 @@ int main(int argc, char **argv, char **envp)
                             exit(1);
                         }
                     } else {
-                        semihosting_target = SEMIHOSTING_TARGET_AUTO;
+                        semihosting.target = SEMIHOSTING_TARGET_AUTO;
                     }
                 } else {
                     fprintf(stderr, "Unsupported semihosting-config %s\n",
