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.l...@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(-) 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, -- 1.7.2.3