Patchwork target-arm: return the right exit code when using semi-hosting.

login
register
mail settings
Submitter Christophe LYON
Date Feb. 18, 2011, 3:45 p.m.
Message ID <1298043923-13787-1-git-send-email-christophe.lyon@st.com>
Download mbox | patch
Permalink /patch/83620/
State New
Headers show

Comments

Christophe LYON - Feb. 18, 2011, 3:45 p.m.
On ARM, the SYS_EXIT semi-hosting call has no room for application
exit code, hence exiting a program from qemu always returns 0.

This patch catches to argument passed to exit() and uses it as the
return code when processing SYS_EXIT.

Signed-off-by: Christophe Lyon <christophe.lyon@st.com>
---
 arm-semi.c           |    6 ++++--
 cpu-exec.c           |   23 +++++++++++++++++++++++
 hw/elf_ops.h         |   13 +++++++++++++
 hw/loader.c          |    2 ++
 linux-user/elfload.c |   20 ++++++++++++++++++++
 5 files changed, 62 insertions(+), 2 deletions(-)
Peter Maydell - Feb. 18, 2011, 4:13 p.m.
On 18 February 2011 15:45, Christophe Lyon <christophe.lyon@st.com> wrote:
> On ARM, the SYS_EXIT semi-hosting call has no room for application
> exit code, hence exiting a program from qemu always returns 0.
>
> This patch catches to argument passed to exit() and uses it as the
> return code when processing SYS_EXIT.

I'm afraid you've just run into the limitations of semihosting
as an API again: it doesn't provide a way for programs to pass
out an exit code, and trying to shoehorn back doors for this
into qemu just results in ugly code in qemu. The qemu
implementation of semihosting should implement the semihosting
API, not semihosting with weird undocumented extras.

If you care about this sort of thing then linux-user mode is
probably a better approach, as that is actually designed for
a unixish program to run as-if-natively.

-- PMM
Christophe LYON - Feb. 18, 2011, 4:29 p.m.
On 18.02.2011 17:13, Peter Maydell wrote:
> 
> I'm afraid you've just run into the limitations of semihosting
> as an API again: it doesn't provide a way for programs to pass
Indeed.

> out an exit code, and trying to shoehorn back doors for this
> into qemu just results in ugly code in qemu. The qemu
> implementation of semihosting should implement the semihosting
> API, not semihosting with weird undocumented extras.

Well, exit() is documented in the C ISO standard, so using its argument as exit code is doing what the user expects.
 
> If you care about this sort of thing then linux-user mode is
> probably a better approach, as that is actually designed for
> a unixish program to run as-if-natively.

We are using qemu to execute programs compiled in bare machine mode (eg with arm-none-eabi-gcc as opposed to arm-none-linux-gnueabi-gcc), where semihosting is used to communicate with the host environment. And we lack support for exit code.

Christophe.
Peter Maydell - Feb. 18, 2011, 5:38 p.m.
On 18 February 2011 16:29, Christophe Lyon <christophe.lyon@st.com> wrote:
> We are using qemu to execute programs compiled in bare machine mode
> (eg with arm-none-eabi-gcc as opposed to arm-none-linux-gnueabi-gcc),
> where semihosting is used to communicate with the host environment.
> And we lack support for exit code.

You need to argue for improvements to the semihosting API/ABI
via whatever ARM support channels, I'm afraid.

-- PMM

Patch

diff --git a/arm-semi.c b/arm-semi.c
index 1d5179b..4ef3769 100644
--- a/arm-semi.c
+++ b/arm-semi.c
@@ -166,6 +166,8 @@  static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err)
 #endif
 }
 
+extern target_ulong arm_exit_code;
+
 #define ARG(n)					\
 ({						\
     target_ulong __arg;				\
@@ -478,8 +480,8 @@  uint32_t do_arm_semihosting(CPUState *env)
             return 0;
         }
     case SYS_EXIT:
-        gdb_exit(env, 0);
-        exit(0);
+        gdb_exit(env, arm_exit_code);
+        exit(arm_exit_code);
     default:
         fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
         cpu_dump_state(env, stderr, fprintf, 0);
diff --git a/cpu-exec.c b/cpu-exec.c
index b03b3a7..e1eac64 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -198,6 +198,14 @@  static inline TranslationBlock *tb_find_fast(void)
 
 /* main execution loop */
 
+/* On ARM, semi-hosting has no room for application exit code. To work
+   around this, when we start executing exit(), we take note of its
+   parameter, which will be used as return code.  */
+target_ulong addr_of_exit = 0;
+#if defined(TARGET_ARM)
+target_ulong arm_exit_code = 0;
+#endif
+
 volatile sig_atomic_t exit_request;
 
 int cpu_exec(CPUState *env1)
@@ -208,6 +216,10 @@  int cpu_exec(CPUState *env1)
     uint8_t *tc_ptr;
     unsigned long next_tb;
 
+#if defined(TARGET_ARM)
+    static int arm_exit_reached = 0;
+#endif
+
     if (cpu_halted(env1) == EXCP_HALTED)
         return EXCP_HALTED;
 
@@ -544,6 +556,17 @@  int cpu_exec(CPUState *env1)
 #endif /* DEBUG_DISAS || CONFIG_DEBUG_EXEC */
                 spin_lock(&tb_lock);
                 tb = tb_find_fast();
+
+#if defined(TARGET_ARM)
+                /* When we reach exit(), make a copy of the
+                   application exit code.  */
+                if ((tb->pc == addr_of_exit)
+                    && (arm_exit_reached == 0)) {
+                    arm_exit_code = env->regs[0];
+                    arm_exit_reached = 1;
+                }
+#endif
+
                 /* Note: we do it here to avoid a gcc bug on Mac OS X when
                    doing it in tb_find_slow */
                 if (tb_invalidated_flag) {
diff --git a/hw/elf_ops.h b/hw/elf_ops.h
index 0bd7235..373c8cd 100644
--- a/hw/elf_ops.h
+++ b/hw/elf_ops.h
@@ -181,6 +181,19 @@  static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
     s->next = syminfos;
     syminfos = s;
     qemu_free(shdr_table);
+
+    /* Take note of the address of the exit() function, to speed up
+       exit() calls tracking. This is currently used only for ARM, but
+       target-dependent code is not allowed in this module.  */
+    i = 0;
+    while (i < nsyms) {
+        if (strcmp("exit", s->disas_strtab + syms[i].st_name) == 0) {
+            addr_of_exit = syms[i].st_value;
+            break;
+        }
+        i++;
+    }
+
     return 0;
  fail:
     qemu_free(syms);
diff --git a/hw/loader.c b/hw/loader.c
index 35d792e..3ee9986 100644
--- a/hw/loader.c
+++ b/hw/loader.c
@@ -249,6 +249,8 @@  static void *load_at(int fd, int offset, int size)
 #define ELF_CLASS   ELFCLASS32
 #include "elf.h"
 
+extern int addr_of_exit;
+
 #define SZ		32
 #define elf_word        uint32_t
 #define elf_sword        int32_t
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 2de83e4..fa460c8 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -1455,9 +1455,14 @@  static void load_elf_image(const char *image_name, int image_fd,
         info->brk = info->end_code;
     }
 
+#if !defined(TARGET_ARM)
+    /* On ARM, we want symbols in order to catch exit() calls.  */
     if (qemu_log_enabled()) {
+#endif
         load_symbols(ehdr, image_fd, load_bias);
+#if !defined(TARGET_ARM)
     }
+#endif
 
     close(image_fd);
     return;
@@ -1545,6 +1550,8 @@  static int symcmp(const void *s0, const void *s1)
         : ((sym0->st_value > sym1->st_value) ? 1 : 0);
 }
 
+extern target_ulong addr_of_exit;
+
 /* Best attempt to load symbols from this ELF object. */
 static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
 {
@@ -1641,6 +1648,19 @@  static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias)
     s->lookup_symbol = lookup_symbolxx;
     s->next = syminfos;
     syminfos = s;
+
+    /* Take note of the address of the exit() function, to speed up
+       exit() calls tracking. This is currently used only for ARM, but
+       this code fragment must remain target-independent so that it is
+       in sync with hw/elf_ops.h.  */
+    i = 0;
+    while (i < nsyms) {
+        if (strcmp("exit", s->disas_strtab + syms[i].st_name) == 0) {
+            addr_of_exit = syms[i].st_value;
+            break;
+        }
+        i++;
+    }
 }
 
 int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,