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,
