Fix Thumb-1 BE32 execution and disassembly.

Thumb-1 code has some issues in BE32 mode (as currently implemented). In
short, since bytes are swapped within words at load time for BE32
executables, this also swaps pairs of adjacent Thumb-1 instructions.

This patch un-swaps those pairs of instructions again, both for execution,
and for disassembly. (The previous version of the patch always read four
bytes in arm_read_memory_func and then extracted the proper two bytes,
in a probably misguided attempt to match the behaviour of actual hardware
as described by e.g. the ARM9TDMI TRM, section 3.3 "Endian effects for
instruction fetches". It's less complicated to just read the correct
two bytes though.)

Signed-off-by: Julian Brown <julian@codesourcery.com>
Message-id: ca20462a044848000370318a8bd41dd0a4ed273f.1484929304.git.julian@codesourcery.com
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Julian Brown 2017-02-07 18:29:59 +00:00 committed by Peter Maydell
parent 3a062d5730
commit f7478a92dd
4 changed files with 40 additions and 1 deletions

View File

@ -190,6 +190,7 @@ void target_disas(FILE *out, CPUState *cpu, target_ulong code,
s.cpu = cpu;
s.info.read_memory_func = target_read_memory;
s.info.read_memory_inner_func = NULL;
s.info.buffer_vma = code;
s.info.buffer_length = size;
s.info.print_address_func = generic_print_address;

View File

@ -295,6 +295,7 @@ typedef struct disassemble_info {
The bottom 16 bits are for the internal use of the disassembler. */
unsigned long flags;
#define INSN_HAS_RELOC 0x80000000
#define INSN_ARM_BE32 0x00010000
PTR private_data;
/* Function used to get bytes to disassemble. MEMADDR is the
@ -306,6 +307,12 @@ typedef struct disassemble_info {
(bfd_vma memaddr, bfd_byte *myaddr, int length,
struct disassemble_info *info);
/* A place to stash the real read_memory_func if read_memory_func wants to
do some funky address arithmetic or similar (e.g. for ARM BE32 mode). */
int (*read_memory_inner_func)
(bfd_vma memaddr, bfd_byte *myaddr, int length,
struct disassemble_info *info);
/* Function which should be called if we get an error that we can't
recover from. STATUS is the errno value from read_memory_func and
MEMADDR is the address that we were trying to read. INFO is a

View File

@ -39,7 +39,15 @@ static inline uint32_t arm_ldl_code(CPUARMState *env, target_ulong addr,
static inline uint16_t arm_lduw_code(CPUARMState *env, target_ulong addr,
bool sctlr_b)
{
uint16_t insn = cpu_lduw_code(env, addr);
uint16_t insn;
#ifndef CONFIG_USER_ONLY
/* In big-endian (BE32) mode, adjacent Thumb instructions have been swapped
within each word. Undo that now. */
if (sctlr_b) {
addr ^= 2;
}
#endif
insn = cpu_lduw_code(env, addr);
if (bswap_code(sctlr_b)) {
return bswap16(insn);
}

View File

@ -446,6 +446,21 @@ print_insn_thumb1(bfd_vma pc, disassemble_info *info)
return print_insn_arm(pc | 1, info);
}
static int arm_read_memory_func(bfd_vma memaddr, bfd_byte *b,
int length, struct disassemble_info *info)
{
assert(info->read_memory_inner_func);
assert((info->flags & INSN_ARM_BE32) == 0 || length == 2 || length == 4);
if ((info->flags & INSN_ARM_BE32) != 0 && length == 2) {
assert(info->endian == BFD_ENDIAN_LITTLE);
return info->read_memory_inner_func(memaddr ^ 2, (bfd_byte *)b, 2,
info);
} else {
return info->read_memory_inner_func(memaddr, b, length, info);
}
}
static void arm_disas_set_info(CPUState *cpu, disassemble_info *info)
{
ARMCPU *ac = ARM_CPU(cpu);
@ -471,6 +486,14 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info)
info->endian = BFD_ENDIAN_BIG;
#endif
}
if (info->read_memory_inner_func == NULL) {
info->read_memory_inner_func = info->read_memory_func;
info->read_memory_func = arm_read_memory_func;
}
info->flags &= ~INSN_ARM_BE32;
if (arm_sctlr_b(env)) {
info->flags |= INSN_ARM_BE32;
}
}
static void arm_cpu_initfn(Object *obj)