diff --git a/disas.c b/disas.c index 94858241de..4f2c4e4cb9 100644 --- a/disas.c +++ b/disas.c @@ -138,7 +138,7 @@ print_insn_thumb1(bfd_vma pc, disassemble_info *info) /* Disassemble this for me please... (debugging). 'flags' has the following values: i386 - 1 means 16 bit code, 2 means 64 bit code - arm - nonzero means thumb code + arm - bit 0 = thumb, bit 1 = reverse endian ppc - nonzero means little endian other targets - unused */ @@ -169,10 +169,18 @@ void target_disas(FILE *out, target_ulong code, target_ulong size, int flags) disasm_info.mach = bfd_mach_i386_i386; print_insn = print_insn_i386; #elif defined(TARGET_ARM) - if (flags) - print_insn = print_insn_thumb1; - else - print_insn = print_insn_arm; + if (flags & 1) { + print_insn = print_insn_thumb1; + } else { + print_insn = print_insn_arm; + } + if (flags & 2) { +#ifdef TARGET_WORDS_BIGENDIAN + disasm_info.endian = BFD_ENDIAN_LITTLE; +#else + disasm_info.endian = BFD_ENDIAN_BIG; +#endif + } #elif defined(TARGET_SPARC) print_insn = print_insn_sparc; #ifdef TARGET_SPARC64 diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4ce97434e5..f3b1552e9e 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1576,6 +1576,7 @@ static void load_elf_image(const char *image_name, int image_fd, info->start_data = -1; info->end_data = 0; info->brk = 0; + info->elf_flags = ehdr->e_flags; for (i = 0; i < ehdr->e_phnum; i++) { struct elf_phdr *eppnt = phdr + i; diff --git a/linux-user/main.c b/linux-user/main.c index 23ad357b50..191b75060d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -33,6 +33,7 @@ #include "tcg.h" #include "qemu-timer.h" #include "envlist.h" +#include "elf.h" #define DEBUG_LOGFILE "/tmp/qemu.log" @@ -474,6 +475,22 @@ void cpu_loop(CPUX86State *env) #ifdef TARGET_ARM +#define get_user_code_u32(x, gaddr, doswap) \ + ({ abi_long __r = get_user_u32((x), (gaddr)); \ + if (!__r && (doswap)) { \ + (x) = bswap32(x); \ + } \ + __r; \ + }) + +#define get_user_code_u16(x, gaddr, doswap) \ + ({ abi_long __r = get_user_u16((x), (gaddr)); \ + if (!__r && (doswap)) { \ + (x) = bswap16(x); \ + } \ + __r; \ + }) + /* * See the Linux kernel's Documentation/arm/kernel_user_helpers.txt * Input: @@ -707,7 +724,7 @@ void cpu_loop(CPUARMState *env) /* we handle the FPU emulation here, as Linux */ /* we get the opcode */ /* FIXME - what to do if get_user() fails? */ - get_user_u32(opcode, env->regs[15]); + get_user_code_u32(opcode, env->regs[15], env->bswap_code); rc = EmulateAll(opcode, &ts->fpa, env); if (rc == 0) { /* illegal instruction */ @@ -777,23 +794,25 @@ void cpu_loop(CPUARMState *env) if (trapnr == EXCP_BKPT) { if (env->thumb) { /* FIXME - what to do if get_user() fails? */ - get_user_u16(insn, env->regs[15]); + get_user_code_u16(insn, env->regs[15], env->bswap_code); n = insn & 0xff; env->regs[15] += 2; } else { /* FIXME - what to do if get_user() fails? */ - get_user_u32(insn, env->regs[15]); + get_user_code_u32(insn, env->regs[15], env->bswap_code); n = (insn & 0xf) | ((insn >> 4) & 0xff0); env->regs[15] += 4; } } else { if (env->thumb) { /* FIXME - what to do if get_user() fails? */ - get_user_u16(insn, env->regs[15] - 2); + get_user_code_u16(insn, env->regs[15] - 2, + env->bswap_code); n = insn & 0xff; } else { /* FIXME - what to do if get_user() fails? */ - get_user_u32(insn, env->regs[15] - 4); + get_user_code_u32(insn, env->regs[15] - 4, + env->bswap_code); n = insn & 0xffffff; } } @@ -3657,6 +3676,11 @@ int main(int argc, char **argv, char **envp) for(i = 0; i < 16; i++) { env->regs[i] = regs->uregs[i]; } + /* Enable BE8. */ + if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 + && (info->elf_flags & EF_ARM_BE8)) { + env->bswap_code = 1; + } } #elif defined(TARGET_UNICORE32) { diff --git a/linux-user/qemu.h b/linux-user/qemu.h index dd74cc0510..7b299b7bc3 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -51,6 +51,7 @@ struct image_info { abi_ulong auxv_len; abi_ulong arg_start; abi_ulong arg_end; + uint32_t elf_flags; int personality; #ifdef CONFIG_USE_FDPIC abi_ulong loadmap_addr; diff --git a/target-arm/cpu.h b/target-arm/cpu.h index e176c5f65c..c208c804aa 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -216,6 +216,9 @@ typedef struct CPUARMState { uint32_t cregs[16]; } iwmmxt; + /* For mixed endian mode. */ + bool bswap_code; + #if defined(CONFIG_USER_ONLY) /* For usermode syscall translation. */ int eabi; @@ -491,7 +494,9 @@ static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp) #define ARM_TBFLAG_VFPEN_MASK (1 << ARM_TBFLAG_VFPEN_SHIFT) #define ARM_TBFLAG_CONDEXEC_SHIFT 8 #define ARM_TBFLAG_CONDEXEC_MASK (0xff << ARM_TBFLAG_CONDEXEC_SHIFT) -/* Bits 31..16 are currently unused. */ +#define ARM_TBFLAG_BSWAP_CODE_SHIFT 16 +#define ARM_TBFLAG_BSWAP_CODE_MASK (1 << ARM_TBFLAG_BSWAP_CODE_SHIFT) +/* Bits 31..17 are currently unused. */ /* some convenience accessor macros */ #define ARM_TBFLAG_THUMB(F) \ @@ -506,6 +511,8 @@ static inline void cpu_clone_regs(CPUARMState *env, target_ulong newsp) (((F) & ARM_TBFLAG_VFPEN_MASK) >> ARM_TBFLAG_VFPEN_SHIFT) #define ARM_TBFLAG_CONDEXEC(F) \ (((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT) +#define ARM_TBFLAG_BSWAP_CODE(F) \ + (((F) & ARM_TBFLAG_BSWAP_CODE_MASK) >> ARM_TBFLAG_BSWAP_CODE_SHIFT) static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, target_ulong *cs_base, int *flags) @@ -516,7 +523,8 @@ static inline void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, *flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT) | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT) | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT) - | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT); + | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT) + | (env->bswap_code << ARM_TBFLAG_BSWAP_CODE_SHIFT); if (arm_feature(env, ARM_FEATURE_M)) { privmode = !((env->v7m.exception == 0) && (env->v7m.control & 1)); } else { @@ -543,4 +551,24 @@ static inline void cpu_pc_from_tb(CPUARMState *env, TranslationBlock *tb) env->regs[15] = tb->pc; } +/* Load an instruction and return it in the standard little-endian order */ +static inline uint32_t arm_ldl_code(uint32_t addr, bool do_swap) +{ + uint32_t insn = ldl_code(addr); + if (do_swap) { + return bswap32(insn); + } + return insn; +} + +/* Ditto, for a halfword (Thumb) instruction */ +static inline uint16_t arm_lduw_code(uint32_t addr, bool do_swap) +{ + uint16_t insn = lduw_code(addr); + if (do_swap) { + return bswap16(insn); + } + return insn; +} + #endif diff --git a/target-arm/helper.c b/target-arm/helper.c index d974b579dc..28f127baf8 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -842,7 +842,7 @@ static void do_interrupt_v7m(CPUARMState *env) case EXCP_BKPT: if (semihosting_enabled) { int nr; - nr = lduw_code(env->regs[15]) & 0xff; + nr = arm_lduw_code(env->regs[15], env->bswap_code) & 0xff; if (nr == 0xab) { env->regs[15] += 2; env->regs[0] = do_arm_semihosting(env); @@ -914,9 +914,10 @@ void do_interrupt(CPUARMState *env) if (semihosting_enabled) { /* Check for semihosting interrupt. */ if (env->thumb) { - mask = lduw_code(env->regs[15] - 2) & 0xff; + mask = arm_lduw_code(env->regs[15] - 2, env->bswap_code) & 0xff; } else { - mask = ldl_code(env->regs[15] - 4) & 0xffffff; + mask = arm_ldl_code(env->regs[15] - 4, env->bswap_code) + & 0xffffff; } /* Only intercept calls from privileged modes, to provide some semblance of security. */ @@ -936,7 +937,7 @@ void do_interrupt(CPUARMState *env) case EXCP_BKPT: /* See if this is a semihosting syscall. */ if (env->thumb && semihosting_enabled) { - mask = lduw_code(env->regs[15]) & 0xff; + mask = arm_lduw_code(env->regs[15], env->bswap_code) & 0xff; if (mask == 0xab && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) { env->regs[15] += 2; diff --git a/target-arm/translate.c b/target-arm/translate.c index 46d1d3ef9f..7a3c7d650c 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -59,6 +59,7 @@ typedef struct DisasContext { struct TranslationBlock *tb; int singlestep_enabled; int thumb; + int bswap_code; #if !defined(CONFIG_USER_ONLY) int user; #endif @@ -6705,7 +6706,7 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) TCGv addr; TCGv_i64 tmp64; - insn = ldl_code(s->pc); + insn = arm_ldl_code(s->pc, s->bswap_code); s->pc += 4; /* M variants do not implement ARM mode. */ @@ -8133,7 +8134,7 @@ static int disas_thumb2_insn(CPUARMState *env, DisasContext *s, uint16_t insn_hw /* Fall through to 32-bit decode. */ } - insn = lduw_code(s->pc); + insn = arm_lduw_code(s->pc, s->bswap_code); s->pc += 2; insn |= (uint32_t)insn_hw1 << 16; @@ -9163,7 +9164,7 @@ static void disas_thumb_insn(CPUARMState *env, DisasContext *s) } } - insn = lduw_code(s->pc); + insn = arm_lduw_code(s->pc, s->bswap_code); s->pc += 2; switch (insn >> 12) { @@ -9872,6 +9873,7 @@ static inline void gen_intermediate_code_internal(CPUARMState *env, dc->singlestep_enabled = env->singlestep_enabled; dc->condjmp = 0; dc->thumb = ARM_TBFLAG_THUMB(tb->flags); + dc->bswap_code = ARM_TBFLAG_BSWAP_CODE(tb->flags); dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; #if !defined(CONFIG_USER_ONLY) @@ -10105,7 +10107,8 @@ done_generating: if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(pc_start, dc->pc - pc_start, dc->thumb); + log_target_disas(pc_start, dc->pc - pc_start, + dc->thumb | (dc->bswap_code << 1)); qemu_log("\n"); } #endif